1f5e8e689SIngo Weinhold /*
2c13744f4SIngo Weinhold * Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3f5e8e689SIngo Weinhold * Distributed under the terms of the MIT License.
4f5e8e689SIngo Weinhold */
5f5e8e689SIngo Weinhold
6f5e8e689SIngo Weinhold #include <errno.h>
7f5e8e689SIngo Weinhold #include <getopt.h>
8f5e8e689SIngo Weinhold #include <pwd.h>
9f5e8e689SIngo Weinhold #include <shadow.h>
10f5e8e689SIngo Weinhold #include <stdio.h>
11f5e8e689SIngo Weinhold #include <stdlib.h>
12f5e8e689SIngo Weinhold #include <string.h>
13f5e8e689SIngo Weinhold #include <termios.h>
1489d327d6SIngo Weinhold #include <time.h>
15f5e8e689SIngo Weinhold #include <unistd.h>
16f5e8e689SIngo Weinhold
17f5e8e689SIngo Weinhold #include <OS.h>
18f5e8e689SIngo Weinhold #include <parsedate.h>
19f5e8e689SIngo Weinhold
20f5e8e689SIngo Weinhold #include <RegistrarDefs.h>
21f5e8e689SIngo Weinhold #include <user_group.h>
22f5e8e689SIngo Weinhold #include <util/KMessage.h>
23f5e8e689SIngo Weinhold
24f5e8e689SIngo Weinhold #include <AutoDeleter.h>
25f5e8e689SIngo Weinhold
26f5e8e689SIngo Weinhold #include "multiuser_utils.h"
27f5e8e689SIngo Weinhold
28f5e8e689SIngo Weinhold
29f5e8e689SIngo Weinhold extern const char *__progname;
30f5e8e689SIngo Weinhold
31f5e8e689SIngo Weinhold
32f5e8e689SIngo Weinhold static const char* kUsage =
33c13744f4SIngo Weinhold "Usage: %s [ <options> ] <user name>\n"
34c13744f4SIngo Weinhold "Creates a new user <user name>.\n"
35c13744f4SIngo Weinhold "\n"
36c13744f4SIngo Weinhold "Options:\n"
37c13744f4SIngo Weinhold " -d <home>\n"
38c13744f4SIngo Weinhold " Specifies the home directory for the new user.\n"
39c13744f4SIngo Weinhold " -e <expiration>\n"
40c13744f4SIngo Weinhold " Specifies the expiration date for the new user's account.\n"
41c13744f4SIngo Weinhold " -f <inactive>\n"
42c13744f4SIngo Weinhold " Specifies the number of days after the expiration of the new user's "
43c13744f4SIngo Weinhold "password\n"
44c13744f4SIngo Weinhold " until the account expires.\n"
45c13744f4SIngo Weinhold " -g <gid>\n"
46c13744f4SIngo Weinhold " Specifies the new user's primary group by ID or name.\n"
47c13744f4SIngo Weinhold " -h, --help\n"
48c13744f4SIngo Weinhold " Print usage info.\n"
49c13744f4SIngo Weinhold " -s <shell>\n"
50c13744f4SIngo Weinhold " Specifies the new user's login shell.\n"
51c13744f4SIngo Weinhold " -n <real name>\n"
52c13744f4SIngo Weinhold " Specifies the new user's real name.\n"
53f5e8e689SIngo Weinhold ;
54f5e8e689SIngo Weinhold
55f5e8e689SIngo Weinhold static void
print_usage_and_exit(bool error)56f5e8e689SIngo Weinhold print_usage_and_exit(bool error)
57f5e8e689SIngo Weinhold {
58f5e8e689SIngo Weinhold fprintf(error ? stderr : stdout, kUsage, __progname);
59f5e8e689SIngo Weinhold exit(error ? 1 : 0);
60f5e8e689SIngo Weinhold }
61f5e8e689SIngo Weinhold
62f5e8e689SIngo Weinhold
63f5e8e689SIngo Weinhold int
main(int argc,const char * const * argv)64f5e8e689SIngo Weinhold main(int argc, const char* const* argv)
65f5e8e689SIngo Weinhold {
66f5e8e689SIngo Weinhold const char* home = "/boot/home";
67f5e8e689SIngo Weinhold int expiration = 99999;
68f5e8e689SIngo Weinhold int inactive = -1;
690e9e586cSIngo Weinhold const char* group = NULL;
70f5e8e689SIngo Weinhold const char* shell = "/bin/sh";
71f5e8e689SIngo Weinhold const char* realName = "";
72f5e8e689SIngo Weinhold
73f5e8e689SIngo Weinhold int min = -1;
74f5e8e689SIngo Weinhold int max = -1;
75f5e8e689SIngo Weinhold int warn = 7;
76f5e8e689SIngo Weinhold
77f5e8e689SIngo Weinhold while (true) {
78f5e8e689SIngo Weinhold static struct option sLongOptions[] = {
79f5e8e689SIngo Weinhold { "help", no_argument, 0, 'h' },
80f5e8e689SIngo Weinhold { 0, 0, 0, 0 }
81f5e8e689SIngo Weinhold };
82f5e8e689SIngo Weinhold
83f5e8e689SIngo Weinhold opterr = 0; // don't print errors
84f5e8e689SIngo Weinhold int c = getopt_long(argc, (char**)argv, "d:e:f:g:hn:s:", sLongOptions,
85f5e8e689SIngo Weinhold NULL);
86f5e8e689SIngo Weinhold if (c == -1)
87f5e8e689SIngo Weinhold break;
88f5e8e689SIngo Weinhold
89f5e8e689SIngo Weinhold
90f5e8e689SIngo Weinhold switch (c) {
91f5e8e689SIngo Weinhold case 'd':
92f5e8e689SIngo Weinhold home = optarg;
93f5e8e689SIngo Weinhold break;
94f5e8e689SIngo Weinhold
95f5e8e689SIngo Weinhold case 'e':
96f5e8e689SIngo Weinhold expiration = parsedate(optarg, time(NULL)) / (3600 * 24);
97f5e8e689SIngo Weinhold break;
98f5e8e689SIngo Weinhold
99f5e8e689SIngo Weinhold case 'f':
100f5e8e689SIngo Weinhold inactive = atoi(optarg);
101f5e8e689SIngo Weinhold break;
102f5e8e689SIngo Weinhold
103f5e8e689SIngo Weinhold case 'g':
1040e9e586cSIngo Weinhold {
1050e9e586cSIngo Weinhold group = optarg;
106f5e8e689SIngo Weinhold break;
1070e9e586cSIngo Weinhold }
108f5e8e689SIngo Weinhold
109f5e8e689SIngo Weinhold case 'h':
110f5e8e689SIngo Weinhold print_usage_and_exit(false);
111f5e8e689SIngo Weinhold break;
112f5e8e689SIngo Weinhold
113f5e8e689SIngo Weinhold case 'n':
114f5e8e689SIngo Weinhold realName = optarg;
115f5e8e689SIngo Weinhold break;
116f5e8e689SIngo Weinhold
117f5e8e689SIngo Weinhold case 's':
118f5e8e689SIngo Weinhold shell = optarg;
119f5e8e689SIngo Weinhold break;
120f5e8e689SIngo Weinhold
121f5e8e689SIngo Weinhold default:
122f5e8e689SIngo Weinhold print_usage_and_exit(true);
123f5e8e689SIngo Weinhold break;
124f5e8e689SIngo Weinhold }
125f5e8e689SIngo Weinhold }
126f5e8e689SIngo Weinhold
127f5e8e689SIngo Weinhold if (optind != argc - 1)
128f5e8e689SIngo Weinhold print_usage_and_exit(true);
129f5e8e689SIngo Weinhold
130f5e8e689SIngo Weinhold const char* user = argv[optind];
131f5e8e689SIngo Weinhold
132f5e8e689SIngo Weinhold if (geteuid() != 0) {
133*032ea9a4SIngo Weinhold fprintf(stderr, "Error: Only root may add users.\n");
134f5e8e689SIngo Weinhold exit(1);
135f5e8e689SIngo Weinhold }
136f5e8e689SIngo Weinhold
137f5e8e689SIngo Weinhold // check, if user already exists
138f5e8e689SIngo Weinhold if (getpwnam(user) != NULL) {
139f5e8e689SIngo Weinhold fprintf(stderr, "Error: User \"%s\" already exists.\n", user);
140f5e8e689SIngo Weinhold exit(1);
141f5e8e689SIngo Weinhold }
142f5e8e689SIngo Weinhold
1430e9e586cSIngo Weinhold // get group ID
1440e9e586cSIngo Weinhold gid_t gid = 100;
1450e9e586cSIngo Weinhold if (group != NULL) {
1460e9e586cSIngo Weinhold char* end;
1470e9e586cSIngo Weinhold gid = strtol(group, &end, 0);
1480e9e586cSIngo Weinhold if (*end == '\0') {
1490e9e586cSIngo Weinhold // seems to be a number
1500e9e586cSIngo Weinhold if (gid < 1) {
1510e9e586cSIngo Weinhold fprintf(stderr, "Error: Invalid group ID \"%s\".\n",
1520e9e586cSIngo Weinhold group);
1530e9e586cSIngo Weinhold exit(1);
1540e9e586cSIngo Weinhold }
1550e9e586cSIngo Weinhold } else {
1560e9e586cSIngo Weinhold // must be a group name -- get it
1570e9e586cSIngo Weinhold char* buffer = NULL;
1580e9e586cSIngo Weinhold ssize_t bufferSize = sysconf(_SC_GETGR_R_SIZE_MAX);
1590e9e586cSIngo Weinhold if (bufferSize <= 0)
1600e9e586cSIngo Weinhold bufferSize = 256;
1610e9e586cSIngo Weinhold for (;;) {
1620e9e586cSIngo Weinhold buffer = (char*)realloc(buffer, bufferSize);
1630e9e586cSIngo Weinhold if (buffer == NULL) {
1640e9e586cSIngo Weinhold fprintf(stderr, "Error: Out of memory!\n");
1650e9e586cSIngo Weinhold exit(1);
1660e9e586cSIngo Weinhold }
1670e9e586cSIngo Weinhold
1680e9e586cSIngo Weinhold struct group groupBuffer;
1690e9e586cSIngo Weinhold struct group* groupFound;
1700e9e586cSIngo Weinhold int error = getgrnam_r(group, &groupBuffer, buffer, bufferSize,
1710e9e586cSIngo Weinhold &groupFound);
1720e9e586cSIngo Weinhold if (error == ERANGE) {
1730e9e586cSIngo Weinhold bufferSize *= 2;
1740e9e586cSIngo Weinhold continue;
1750e9e586cSIngo Weinhold }
1760e9e586cSIngo Weinhold
1770e9e586cSIngo Weinhold if (error != 0) {
17882a064b5SIngo Weinhold fprintf(stderr, "Error: Failed to get info for group "
17982a064b5SIngo Weinhold "\"%s\".\n", group);
1800e9e586cSIngo Weinhold exit(1);
1810e9e586cSIngo Weinhold }
1820e9e586cSIngo Weinhold if (groupFound == NULL) {
1830e9e586cSIngo Weinhold fprintf(stderr, "Error: Specified group \"%s\" doesn't "
1840e9e586cSIngo Weinhold "exist.\n", group);
1850e9e586cSIngo Weinhold exit(1);
1860e9e586cSIngo Weinhold }
1870e9e586cSIngo Weinhold
1880e9e586cSIngo Weinhold gid = groupFound->gr_gid;
1890e9e586cSIngo Weinhold break;
1900e9e586cSIngo Weinhold }
1910e9e586cSIngo Weinhold }
1920e9e586cSIngo Weinhold }
1930e9e586cSIngo Weinhold
194f5e8e689SIngo Weinhold // find an unused UID
195f5e8e689SIngo Weinhold uid_t uid = 1000;
196f5e8e689SIngo Weinhold while (getpwuid(uid) != NULL)
197f5e8e689SIngo Weinhold uid++;
198f5e8e689SIngo Weinhold
199f5e8e689SIngo Weinhold // prepare request for the registrar
200f5e8e689SIngo Weinhold KMessage message(BPrivate::B_REG_UPDATE_USER);
201f5e8e689SIngo Weinhold if (message.AddInt32("uid", uid) != B_OK
202f5e8e689SIngo Weinhold || message.AddInt32("gid", gid) != B_OK
203f5e8e689SIngo Weinhold || message.AddString("name", user) != B_OK
204f5e8e689SIngo Weinhold || message.AddString("password", "x") != B_OK
205f5e8e689SIngo Weinhold || message.AddString("home", home) != B_OK
206f5e8e689SIngo Weinhold || message.AddString("shell", shell) != B_OK
207f5e8e689SIngo Weinhold || message.AddString("real name", realName) != B_OK
208*032ea9a4SIngo Weinhold || message.AddString("shadow password", "!") != B_OK
20989d327d6SIngo Weinhold || message.AddInt32("last changed", time(NULL)) != B_OK
210f5e8e689SIngo Weinhold || message.AddInt32("min", min) != B_OK
211f5e8e689SIngo Weinhold || message.AddInt32("max", max) != B_OK
212f5e8e689SIngo Weinhold || message.AddInt32("warn", warn) != B_OK
213f5e8e689SIngo Weinhold || message.AddInt32("inactive", inactive) != B_OK
214f5e8e689SIngo Weinhold || message.AddInt32("expiration", expiration) != B_OK
215f5e8e689SIngo Weinhold || message.AddInt32("flags", 0) != B_OK
216f5e8e689SIngo Weinhold || message.AddBool("add user", true) != B_OK) {
217f5e8e689SIngo Weinhold fprintf(stderr, "Error: Out of memory!\n");
218f5e8e689SIngo Weinhold exit(1);
219f5e8e689SIngo Weinhold }
220f5e8e689SIngo Weinhold
221f5e8e689SIngo Weinhold // send the request
222f5e8e689SIngo Weinhold KMessage reply;
223f5e8e689SIngo Weinhold status_t error = send_authentication_request_to_registrar(message, reply);
224f5e8e689SIngo Weinhold if (error != B_OK) {
225f5e8e689SIngo Weinhold fprintf(stderr, "Error: Failed to create user: %s\n", strerror(error));
226f5e8e689SIngo Weinhold exit(1);
227f5e8e689SIngo Weinhold }
228f5e8e689SIngo Weinhold
229f5e8e689SIngo Weinhold return 0;
230f5e8e689SIngo Weinhold }
231