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 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 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