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