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