1 /* 2 * Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@gmx.de. 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 [ <options> ] <user name>\n" 34 "Creates a new user <user name>.\n" 35 "\n" 36 "Options:\n" 37 " -d <home>\n" 38 " Specifies the home directory for the new user.\n" 39 " -e <expiration>\n" 40 " Specifies the expiration date for the new user's account.\n" 41 " -f <inactive>\n" 42 " Specifies the number of days after the expiration of the new user's " 43 "password\n" 44 " until the account expires.\n" 45 " -g <gid>\n" 46 " Specifies the new user's primary group by ID or name.\n" 47 " -h, --help\n" 48 " Print usage info.\n" 49 " -s <shell>\n" 50 " Specifies the new user's login shell.\n" 51 " -n <real name>\n" 52 " Specifies the new user's real name.\n" 53 ; 54 55 static void 56 print_usage_and_exit(bool error) 57 { 58 fprintf(error ? stderr : stdout, kUsage, __progname); 59 exit(error ? 1 : 0); 60 } 61 62 63 int 64 main(int argc, const char* const* argv) 65 { 66 const char* home = "/boot/home"; 67 int expiration = 99999; 68 int inactive = -1; 69 const char* group = NULL; 70 const char* shell = "/bin/sh"; 71 const char* realName = ""; 72 73 int min = -1; 74 int max = -1; 75 int warn = 7; 76 77 while (true) { 78 static struct option sLongOptions[] = { 79 { "help", no_argument, 0, 'h' }, 80 { 0, 0, 0, 0 } 81 }; 82 83 opterr = 0; // don't print errors 84 int c = getopt_long(argc, (char**)argv, "d:e:f:g:hn:s:", sLongOptions, 85 NULL); 86 if (c == -1) 87 break; 88 89 90 switch (c) { 91 case 'd': 92 home = optarg; 93 break; 94 95 case 'e': 96 expiration = parsedate(optarg, time(NULL)) / (3600 * 24); 97 break; 98 99 case 'f': 100 inactive = atoi(optarg); 101 break; 102 103 case 'g': 104 { 105 group = optarg; 106 break; 107 } 108 109 case 'h': 110 print_usage_and_exit(false); 111 break; 112 113 case 'n': 114 realName = optarg; 115 break; 116 117 case 's': 118 shell = optarg; 119 break; 120 121 default: 122 print_usage_and_exit(true); 123 break; 124 } 125 } 126 127 if (optind != argc - 1) 128 print_usage_and_exit(true); 129 130 const char* user = argv[optind]; 131 132 if (geteuid() != 0) { 133 fprintf(stderr, "Error: Only root may add users.\n"); 134 exit(1); 135 } 136 137 // check, if user already exists 138 if (getpwnam(user) != NULL) { 139 fprintf(stderr, "Error: User \"%s\" already exists.\n", user); 140 exit(1); 141 } 142 143 // get group ID 144 gid_t gid = 100; 145 if (group != NULL) { 146 char* end; 147 gid = strtol(group, &end, 0); 148 if (*end == '\0') { 149 // seems to be a number 150 if (gid < 1) { 151 fprintf(stderr, "Error: Invalid group ID \"%s\".\n", 152 group); 153 exit(1); 154 } 155 } else { 156 // must be a group name -- get it 157 char* buffer = NULL; 158 ssize_t bufferSize = sysconf(_SC_GETGR_R_SIZE_MAX); 159 if (bufferSize <= 0) 160 bufferSize = 256; 161 for (;;) { 162 buffer = (char*)realloc(buffer, bufferSize); 163 if (buffer == NULL) { 164 fprintf(stderr, "Error: Out of memory!\n"); 165 exit(1); 166 } 167 168 struct group groupBuffer; 169 struct group* groupFound; 170 int error = getgrnam_r(group, &groupBuffer, buffer, bufferSize, 171 &groupFound); 172 if (error == ERANGE) { 173 bufferSize *= 2; 174 continue; 175 } 176 177 if (error != 0) { 178 fprintf(stderr, "Error: Failed to get info for group " 179 "\"%s\".\n", group); 180 exit(1); 181 } 182 if (groupFound == NULL) { 183 fprintf(stderr, "Error: Specified group \"%s\" doesn't " 184 "exist.\n", group); 185 exit(1); 186 } 187 188 gid = groupFound->gr_gid; 189 break; 190 } 191 } 192 } 193 194 // find an unused UID 195 uid_t uid = 1000; 196 while (getpwuid(uid) != NULL) 197 uid++; 198 199 // prepare request for the registrar 200 KMessage message(BPrivate::B_REG_UPDATE_USER); 201 if (message.AddInt32("uid", uid) != B_OK 202 || message.AddInt32("gid", gid) != B_OK 203 || message.AddString("name", user) != B_OK 204 || message.AddString("password", "x") != B_OK 205 || message.AddString("home", home) != B_OK 206 || message.AddString("shell", shell) != B_OK 207 || message.AddString("real name", realName) != B_OK 208 || message.AddString("shadow password", "!") != B_OK 209 || message.AddInt32("last changed", time(NULL)) != B_OK 210 || message.AddInt32("min", min) != B_OK 211 || message.AddInt32("max", max) != B_OK 212 || message.AddInt32("warn", warn) != B_OK 213 || message.AddInt32("inactive", inactive) != B_OK 214 || message.AddInt32("expiration", expiration) != B_OK 215 || message.AddInt32("flags", 0) != B_OK 216 || message.AddBool("add user", true) != B_OK) { 217 fprintf(stderr, "Error: Out of memory!\n"); 218 exit(1); 219 } 220 221 // send the request 222 KMessage reply; 223 status_t error = send_authentication_request_to_registrar(message, reply); 224 if (error != B_OK) { 225 fprintf(stderr, "Error: Failed to create user: %s\n", strerror(error)); 226 exit(1); 227 } 228 229 return 0; 230 } 231