xref: /haiku/src/bin/multiuser/useradd.cpp (revision fc1ca2da5cfcb00ffdf791606d5ae97fdd58a638)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <errno.h>
7 #include <getopt.h>
8 #include <pwd.h>
9 #include <shadow.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <termios.h>
14 #include <unistd.h>
15 
16 #include <OS.h>
17 #include <parsedate.h>
18 
19 #include <RegistrarDefs.h>
20 #include <user_group.h>
21 #include <util/KMessage.h>
22 
23 #include <AutoDeleter.h>
24 
25 #include "multiuser_utils.h"
26 
27 
28 extern const char *__progname;
29 
30 
31 static const char* kUsage =
32 "Usage: %s [ -d <home> ] [ -e <expiration> ] [ -f <inactive> ] [ -g <gid> ]\n"
33 "          [ -s <shell> ] [ -n <real name> ]\n"
34 ;
35 
36 static void
37 print_usage_and_exit(bool error)
38 {
39 	fprintf(error ? stderr : stdout, kUsage, __progname);
40 	exit(error ? 1 : 0);
41 }
42 
43 
44 int
45 main(int argc, const char* const* argv)
46 {
47 	const char* home = "/boot/home";
48 	int expiration = 99999;
49 	int inactive = -1;
50 	gid_t gid = 100;
51 	const char* shell = "/bin/sh";
52 	const char* realName = "";
53 
54 	int min = -1;
55 	int max = -1;
56 	int warn = 7;
57 
58 	while (true) {
59 		static struct option sLongOptions[] = {
60 			{ "help", no_argument, 0, 'h' },
61 			{ 0, 0, 0, 0 }
62 		};
63 
64 		opterr = 0; // don't print errors
65 		int c = getopt_long(argc, (char**)argv, "d:e:f:g:hn:s:", sLongOptions,
66 			NULL);
67 		if (c == -1)
68 			break;
69 
70 
71 		switch (c) {
72 			case 'd':
73 				home = optarg;
74 				break;
75 
76 			case 'e':
77 				expiration = parsedate(optarg, time(NULL)) / (3600 * 24);
78 				break;
79 
80 			case 'f':
81 				inactive = atoi(optarg);
82 				break;
83 
84 			case 'g':
85 				gid = atoi(optarg);
86 				break;
87 
88 			case 'h':
89 				print_usage_and_exit(false);
90 				break;
91 
92 			case 'n':
93 				realName = optarg;
94 				break;
95 
96 			case 's':
97 				shell = optarg;
98 				break;
99 
100 			default:
101 				print_usage_and_exit(true);
102 				break;
103 		}
104 	}
105 
106 	if (optind != argc - 1)
107 		print_usage_and_exit(true);
108 
109 	const char* user = argv[optind];
110 
111 	if (geteuid() != 0) {
112 		fprintf(stderr, "Error: You need to be root.\n");
113 		exit(1);
114 	}
115 
116 	// check, if user already exists
117 	if (getpwnam(user) != NULL) {
118 		fprintf(stderr, "Error: User \"%s\" already exists.\n", user);
119 		exit(1);
120 	}
121 
122 	// read password
123 	char password[LINE_MAX];
124 	if (read_password("password for user: ", password, sizeof(password),
125 			false) != B_OK) {
126 		exit(1);
127 	}
128 
129 	if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
130 		fprintf(stderr, "Error: The password is too long.\n");
131 		exit(1);
132 	}
133 
134 	// read password again
135 	char repeatedPassword[LINE_MAX];
136 	if (read_password("repeat password: ", repeatedPassword,
137 			sizeof(repeatedPassword), false) != B_OK) {
138 		exit(1);
139 	}
140 
141 	// passwords need to match
142 	if (strcmp(password, repeatedPassword) != 0) {
143 		fprintf(stderr, "Error: passwords don't match\n");
144 		exit(1);
145 	}
146 
147 	memset(repeatedPassword, 0, sizeof(repeatedPassword));
148 
149 	// crypt it
150 	char* encryptedPassword;
151 	if (strlen(password) > 0) {
152 		encryptedPassword = crypt(password, user);
153 		memset(password, 0, sizeof(password));
154 	} else
155 		encryptedPassword = password;
156 
157 	// find an unused UID
158 	uid_t uid = 1000;
159 	while (getpwuid(uid) != NULL)
160 		uid++;
161 
162 	// prepare request for the registrar
163 	KMessage message(BPrivate::B_REG_UPDATE_USER);
164 	if (message.AddInt32("uid", uid) != B_OK
165 		|| message.AddInt32("gid", gid) != B_OK
166 		|| message.AddString("name", user) != B_OK
167 		|| message.AddString("password", "x") != B_OK
168 		|| message.AddString("home", home) != B_OK
169 		|| message.AddString("shell", shell) != B_OK
170 		|| message.AddString("real name", realName) != B_OK
171 		|| message.AddString("shadow password", encryptedPassword) != B_OK
172 		|| message.AddInt32("min", min) != B_OK
173 		|| message.AddInt32("max", max) != B_OK
174 		|| message.AddInt32("warn", warn) != B_OK
175 		|| message.AddInt32("inactive", inactive) != B_OK
176 		|| message.AddInt32("expiration", expiration) != B_OK
177 		|| message.AddInt32("flags", 0) != B_OK
178 		|| message.AddBool("add user", true) != B_OK) {
179 		fprintf(stderr, "Error: Out of memory!\n");
180 		exit(1);
181 	}
182 
183 	// send the request
184 	KMessage reply;
185 	status_t error = send_authentication_request_to_registrar(message, reply);
186 	if (error != B_OK) {
187 		fprintf(stderr, "Error: Failed to create user: %s\n", strerror(error));
188 		exit(1);
189 	}
190 
191 	return 0;
192 }
193