Eric Andersen | 27f64e1 | 2002-06-23 04:24:25 +0000 | [diff] [blame^] | 1 | /* vi: set sw=4 ts=4: */ |
| 2 | #include <fcntl.h> |
| 3 | #include <stdio.h> |
| 4 | #include <string.h> |
| 5 | #include <signal.h> |
| 6 | #include <sys/stat.h> |
| 7 | #include <sys/types.h> |
| 8 | #include <unistd.h> |
| 9 | #include <utime.h> |
| 10 | #include <syslog.h> |
| 11 | #include <time.h> |
| 12 | #include <sys/resource.h> |
| 13 | #include <errno.h> |
| 14 | |
| 15 | #include "busybox.h" |
| 16 | |
| 17 | static char crypt_passwd[128]; |
| 18 | |
| 19 | static int create_backup(const char *backup, FILE * fp); |
| 20 | static int new_password(const struct passwd *pw, int amroot, int algo); |
| 21 | static void set_filesize_limit(int blocks); |
| 22 | |
| 23 | |
| 24 | int get_algo(char *a) |
| 25 | { |
| 26 | int x = 0; /* standart: DES */ |
| 27 | |
| 28 | if (strcasecmp(a, "md5") == 0) |
| 29 | x = 1; |
| 30 | return x; |
| 31 | } |
| 32 | |
| 33 | |
| 34 | extern int update_passwd(const struct passwd *pw, char *crypt_pw) |
| 35 | { |
| 36 | char filename[1024]; |
| 37 | char buf[1025]; |
| 38 | char buffer[80]; |
| 39 | char username[32]; |
| 40 | char *pw_rest; |
| 41 | int has_shadow = 0; |
| 42 | int mask; |
| 43 | int continued; |
| 44 | FILE *fp; |
| 45 | FILE *out_fp; |
| 46 | struct stat sb; |
| 47 | struct flock lock; |
| 48 | |
| 49 | if (access(shadow_file, F_OK) == 0) { |
| 50 | has_shadow = 1; |
| 51 | } |
| 52 | if (has_shadow) { |
| 53 | snprintf(filename, sizeof filename, "%s", shadow_file); |
| 54 | } else { |
| 55 | snprintf(filename, sizeof filename, "%s", passwd_file); |
| 56 | } |
| 57 | |
| 58 | if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) { |
| 59 | /* return 0; */ |
| 60 | return 1; |
| 61 | } |
| 62 | |
| 63 | /* Lock the password file before updating */ |
| 64 | lock.l_type = F_WRLCK; |
| 65 | lock.l_whence = SEEK_SET; |
| 66 | lock.l_start = 0; |
| 67 | lock.l_len = 0; |
| 68 | if (fcntl(fileno(fp), F_SETLK, &lock) < 0) { |
| 69 | fprintf(stderr, "%s: %s\n", filename, strerror(errno)); |
| 70 | return 1; |
| 71 | } |
| 72 | lock.l_type = F_UNLCK; |
| 73 | |
| 74 | snprintf(buf, sizeof buf, "%s-", filename); |
| 75 | if (create_backup(buf, fp)) { |
| 76 | fcntl(fileno(fp), F_SETLK, &lock); |
| 77 | fclose(fp); |
| 78 | return 1; |
| 79 | } |
| 80 | snprintf(buf, sizeof buf, "%s+", filename); |
| 81 | mask = umask(0777); |
| 82 | out_fp = fopen(buf, "w"); |
| 83 | umask(mask); |
| 84 | if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777)) |
| 85 | || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) { |
| 86 | fcntl(fileno(fp), F_SETLK, &lock); |
| 87 | fclose(fp); |
| 88 | fclose(out_fp); |
| 89 | return 1; |
| 90 | } |
| 91 | |
| 92 | continued = 0; |
| 93 | snprintf(username, sizeof username, "%s:", pw->pw_name); |
| 94 | rewind(fp); |
| 95 | while (!feof(fp)) { |
| 96 | fgets(buffer, sizeof buffer, fp); |
| 97 | if (!continued) { // Check to see if we're updating this line. |
| 98 | if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match. |
| 99 | pw_rest = strchr(buffer, ':'); |
| 100 | *pw_rest++ = '\0'; |
| 101 | pw_rest = strchr(pw_rest, ':'); |
| 102 | fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest); |
| 103 | } else { |
| 104 | fputs(buffer, out_fp); |
| 105 | } |
| 106 | } else { |
| 107 | fputs(buffer, out_fp); |
| 108 | } |
| 109 | if (buffer[strlen(buffer) - 1] == '\n') { |
| 110 | continued = 0; |
| 111 | } else { |
| 112 | continued = 1; |
| 113 | } |
| 114 | bzero(buffer, sizeof buffer); |
| 115 | } |
| 116 | |
| 117 | if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) { |
| 118 | unlink(buf); |
| 119 | fcntl(fileno(fp), F_SETLK, &lock); |
| 120 | fclose(fp); |
| 121 | return 1; |
| 122 | } |
| 123 | if (rename(buf, filename) < 0) { |
| 124 | fcntl(fileno(fp), F_SETLK, &lock); |
| 125 | fclose(fp); |
| 126 | return 1; |
| 127 | } else { |
| 128 | fcntl(fileno(fp), F_SETLK, &lock); |
| 129 | fclose(fp); |
| 130 | return 0; |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | |
| 135 | extern int passwd_main(int argc, char **argv) |
| 136 | { |
| 137 | int amroot; |
| 138 | char *cp; |
| 139 | char *np; |
| 140 | char *name; |
| 141 | char *myname; |
| 142 | int flag; |
| 143 | int algo = 0; /* -a - password algorithm */ |
| 144 | int lflg = 0; /* -l - lock account */ |
| 145 | int uflg = 0; /* -u - unlock account */ |
| 146 | int dflg = 0; /* -d - delete password */ |
| 147 | const struct passwd *pw; |
| 148 | unsigned short ruid; |
| 149 | |
| 150 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| 151 | const struct spwd *sp; |
| 152 | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| 153 | amroot = (getuid() == 0); |
| 154 | openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); |
| 155 | while ((flag = getopt(argc, argv, "a:dlu")) != EOF) { |
| 156 | switch (flag) { |
| 157 | case 'a': |
| 158 | algo = get_algo(optarg); |
| 159 | break; |
| 160 | case 'd': |
| 161 | dflg++; |
| 162 | break; |
| 163 | case 'l': |
| 164 | lflg++; |
| 165 | break; |
| 166 | case 'u': |
| 167 | uflg++; |
| 168 | break; |
| 169 | default: |
| 170 | show_usage(); |
| 171 | } |
| 172 | } |
| 173 | ruid = getuid(); |
| 174 | pw = (struct passwd *) getpwuid(ruid); |
| 175 | if (!pw) { |
| 176 | error_msg_and_die("Cannot determine your user name.\n"); |
| 177 | } |
| 178 | myname = (char *) xstrdup(pw->pw_name); |
| 179 | if (optind < argc) { |
| 180 | name = argv[optind]; |
| 181 | } else { |
| 182 | name = myname; |
| 183 | } |
| 184 | if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) { |
| 185 | show_usage(); |
| 186 | } |
| 187 | pw = getpwnam(name); |
| 188 | if (!pw) { |
| 189 | error_msg_and_die("Unknown user %s\n", name); |
| 190 | } |
| 191 | if (!amroot && pw->pw_uid != getuid()) { |
| 192 | syslog(LOG_WARNING, "can't change pwd for `%s'", name); |
| 193 | error_msg_and_die("Permission denied.\n"); |
| 194 | } |
| 195 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| 196 | sp = getspnam(name); |
| 197 | if (!sp) { |
| 198 | sp = (struct spwd *) pwd_to_spwd(pw); |
| 199 | } |
| 200 | cp = sp->sp_pwdp; |
| 201 | np = sp->sp_namp; |
| 202 | #else |
| 203 | cp = pw->pw_passwd; |
| 204 | np = name; |
| 205 | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| 206 | |
| 207 | safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); |
| 208 | if (!(dflg || lflg || uflg)) { |
| 209 | if (!amroot) { |
| 210 | if (cp[0] == '!') { |
| 211 | syslog(LOG_WARNING, "password locked for `%s'", np); |
| 212 | error_msg_and_die( "The password for `%s' cannot be changed.\n", np); |
| 213 | } |
| 214 | } |
| 215 | printf("Changing password for %s\n", name); |
| 216 | if (new_password(pw, amroot, algo)) { |
| 217 | error_msg_and_die( "The password for %s is unchanged.\n", name); |
| 218 | } |
| 219 | } else if (lflg) { |
| 220 | if (crypt_passwd[0] != '!') { |
| 221 | memmove(&crypt_passwd[1], crypt_passwd, |
| 222 | sizeof crypt_passwd - 1); |
| 223 | crypt_passwd[sizeof crypt_passwd - 1] = '\0'; |
| 224 | crypt_passwd[0] = '!'; |
| 225 | } |
| 226 | } else if (uflg) { |
| 227 | if (crypt_passwd[0] == '!') { |
| 228 | memmove(crypt_passwd, &crypt_passwd[1], |
| 229 | sizeof crypt_passwd - 1); |
| 230 | } |
| 231 | } else if (dflg) { |
| 232 | crypt_passwd[0] = '\0'; |
| 233 | } |
| 234 | set_filesize_limit(30000); |
| 235 | signal(SIGHUP, SIG_IGN); |
| 236 | signal(SIGINT, SIG_IGN); |
| 237 | signal(SIGQUIT, SIG_IGN); |
| 238 | umask(077); |
| 239 | if (setuid(0)) { |
| 240 | syslog(LOG_ERR, "can't setuid(0)"); |
| 241 | error_msg_and_die( "Cannot change ID to root.\n"); |
| 242 | } |
| 243 | if (!update_passwd(pw, crypt_passwd)) { |
| 244 | syslog(LOG_INFO, "password for `%s' changed by user `%s'", name, |
| 245 | myname); |
| 246 | printf("Password changed.\n"); |
| 247 | } else { |
| 248 | syslog(LOG_WARNING, |
| 249 | "an error occurred updating the password file"); |
| 250 | error_msg_and_die("An error occurred updating the password file.\n"); |
| 251 | } |
| 252 | return (0); |
| 253 | } |
| 254 | |
| 255 | |
| 256 | |
| 257 | static int create_backup(const char *backup, FILE * fp) |
| 258 | { |
| 259 | struct stat sb; |
| 260 | struct utimbuf ub; |
| 261 | FILE *bkfp; |
| 262 | int c, mask; |
| 263 | |
| 264 | if (fstat(fileno(fp), &sb)) |
| 265 | /* return -1; */ |
| 266 | return 1; |
| 267 | |
| 268 | mask = umask(077); |
| 269 | bkfp = fopen(backup, "w"); |
| 270 | umask(mask); |
| 271 | if (!bkfp) |
| 272 | /* return -1; */ |
| 273 | return 1; |
| 274 | |
| 275 | /* TODO: faster copy, not one-char-at-a-time. --marekm */ |
| 276 | rewind(fp); |
| 277 | while ((c = getc(fp)) != EOF) { |
| 278 | if (putc(c, bkfp) == EOF) |
| 279 | break; |
| 280 | } |
| 281 | if (c != EOF || fflush(bkfp)) { |
| 282 | fclose(bkfp); |
| 283 | /* return -1; */ |
| 284 | return 1; |
| 285 | } |
| 286 | if (fclose(bkfp)) |
| 287 | /* return -1; */ |
| 288 | return 1; |
| 289 | |
| 290 | ub.actime = sb.st_atime; |
| 291 | ub.modtime = sb.st_mtime; |
| 292 | utime(backup, &ub); |
| 293 | return 0; |
| 294 | } |
| 295 | |
| 296 | static int i64c(int i) |
| 297 | { |
| 298 | if (i <= 0) |
| 299 | return ('.'); |
| 300 | if (i == 1) |
| 301 | return ('/'); |
| 302 | if (i >= 2 && i < 12) |
| 303 | return ('0' - 2 + i); |
| 304 | if (i >= 12 && i < 38) |
| 305 | return ('A' - 12 + i); |
| 306 | if (i >= 38 && i < 63) |
| 307 | return ('a' - 38 + i); |
| 308 | return ('z'); |
| 309 | } |
| 310 | |
| 311 | static char *crypt_make_salt(void) |
| 312 | { |
| 313 | time_t now; |
| 314 | static unsigned long x; |
| 315 | static char result[3]; |
| 316 | |
| 317 | time(&now); |
| 318 | x += now + getpid() + clock(); |
| 319 | result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077); |
| 320 | result[1] = i64c(((x >> 12) ^ x) & 077); |
| 321 | result[2] = '\0'; |
| 322 | return result; |
| 323 | } |
| 324 | |
| 325 | |
| 326 | static int new_password(const struct passwd *pw, int amroot, int algo) |
| 327 | { |
| 328 | char *clear; |
| 329 | char *cipher; |
| 330 | char *cp; |
| 331 | char orig[200]; |
| 332 | char pass[200]; |
| 333 | time_t start, now; |
| 334 | |
| 335 | if (!amroot && crypt_passwd[0]) { |
| 336 | if (!(clear = getpass("Old password:"))) { |
| 337 | /* return -1; */ |
| 338 | return 1; |
| 339 | } |
| 340 | cipher = pw_encrypt(clear, crypt_passwd); |
| 341 | if (strcmp(cipher, crypt_passwd) != 0) { |
| 342 | syslog(LOG_WARNING, "incorrect password for `%s'", |
| 343 | pw->pw_name); |
| 344 | time(&start); |
| 345 | now = start; |
| 346 | while (difftime(now, start) < FAIL_DELAY) { |
| 347 | sleep(FAIL_DELAY); |
| 348 | time(&now); |
| 349 | } |
| 350 | fprintf(stderr, "Incorrect password.\n"); |
| 351 | /* return -1; */ |
| 352 | return 1; |
| 353 | } |
| 354 | safe_strncpy(orig, clear, sizeof(orig)); |
| 355 | bzero(clear, strlen(clear)); |
| 356 | bzero(cipher, strlen(cipher)); |
| 357 | } else { |
| 358 | orig[0] = '\0'; |
| 359 | } |
| 360 | if (! |
| 361 | (cp = |
| 362 | getpass ("Enter the new password (minimum of 5, maximum of 8 characters)\n"" |
| 363 | Please use a combination of upper and lower case letters and numbers.\nEnter new password: "))) |
| 364 | { |
| 365 | bzero(orig, sizeof orig); |
| 366 | /* return -1; */ |
| 367 | return 1; |
| 368 | } |
| 369 | safe_strncpy(pass, cp, sizeof(pass)); |
| 370 | bzero(cp, strlen(cp)); |
| 371 | /* if (!obscure(orig, pass, pw)) { */ |
| 372 | if (obscure(orig, pass, pw)) { |
| 373 | if (amroot) { |
| 374 | printf("\nWarning: weak password (continuing).\n"); |
| 375 | } else { |
| 376 | /* return -1; */ |
| 377 | return 1; |
| 378 | } |
| 379 | } |
| 380 | if (!(cp = getpass("Re-enter new password: "))) { |
| 381 | bzero(orig, sizeof orig); |
| 382 | /* return -1; */ |
| 383 | return 1; |
| 384 | } |
| 385 | if (strcmp(cp, pass)) { |
| 386 | fprintf(stderr, "Passwords do not match.\n"); |
| 387 | /* return -1; */ |
| 388 | return 1; |
| 389 | } |
| 390 | bzero(cp, strlen(cp)); |
| 391 | bzero(orig, sizeof(orig)); |
| 392 | |
| 393 | if (algo == 1) { |
| 394 | cp = pw_encrypt(pass, "$1$"); |
| 395 | } else |
| 396 | cp = pw_encrypt(pass, crypt_make_salt()); |
| 397 | bzero(pass, sizeof pass); |
| 398 | safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); |
| 399 | return 0; |
| 400 | } |
| 401 | |
| 402 | static void set_filesize_limit(int blocks) |
| 403 | { |
| 404 | struct rlimit rlimit_fsize; |
| 405 | |
| 406 | rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks; |
| 407 | setrlimit(RLIMIT_FSIZE, &rlimit_fsize); |
| 408 | } |