diff options
-rw-r--r-- | libbb/update_passwd.c | 119 | ||||
-rw-r--r-- | loginutils/chpasswd.c | 77 |
2 files changed, 196 insertions, 0 deletions
diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c new file mode 100644 index 000000000..5572db547 --- /dev/null +++ b/libbb/update_passwd.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * update_passwd | ||
4 | * | ||
5 | * update_passwd is a common function for passwd and chpasswd applets; | ||
6 | * it is responsible for updating password file (i.e. /etc/passwd or | ||
7 | * /etc/shadow) for a given user and password. | ||
8 | * | ||
9 | * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org> | ||
10 | */ | ||
11 | |||
12 | #include "libbb.h" | ||
13 | |||
14 | int update_passwd(const char *filename, const char *username, | ||
15 | const char *new_pw) | ||
16 | { | ||
17 | struct stat sb; | ||
18 | struct flock lock; | ||
19 | FILE *old_fp; | ||
20 | FILE *new_fp; | ||
21 | char *new_name; | ||
22 | char *last_char; | ||
23 | unsigned user_len; | ||
24 | int old_fd; | ||
25 | int new_fd; | ||
26 | int i; | ||
27 | int cnt = 0; | ||
28 | int ret = 1; /* failure */ | ||
29 | |||
30 | /* New passwd file, "/etc/passwd+" for now */ | ||
31 | new_name = xasprintf("%s+", filename); | ||
32 | last_char = &new_name[strlen(new_name)-1]; | ||
33 | username = xasprintf("%s:", username); | ||
34 | user_len = strlen(username); | ||
35 | |||
36 | old_fp = fopen(filename, "r+"); | ||
37 | if (!old_fp) | ||
38 | goto free_mem; | ||
39 | old_fd = fileno(old_fp); | ||
40 | |||
41 | /* Try to create "/etc/passwd+". Wait if it exists. */ | ||
42 | i = 30; | ||
43 | do { | ||
44 | // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC? | ||
45 | new_fd = open(new_name, O_WRONLY|O_CREAT|O_EXCL,0600); | ||
46 | if (new_fd >= 0) goto created; | ||
47 | if (errno != EEXIST) break; | ||
48 | usleep(100000); /* 0.1 sec */ | ||
49 | } while (--i); | ||
50 | bb_perror_msg("cannot create '%s'", new_name); | ||
51 | goto close_old_fp; | ||
52 | |||
53 | created: | ||
54 | if (!fstat(old_fd, &sb)) { | ||
55 | fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ | ||
56 | fchown(new_fd, sb.st_uid, sb.st_gid); | ||
57 | } | ||
58 | new_fp = fdopen(new_fd, "w"); | ||
59 | if (!new_fp) { | ||
60 | close(new_fd); | ||
61 | goto unlink_new; | ||
62 | } | ||
63 | |||
64 | /* Backup file is "/etc/passwd-" */ | ||
65 | last_char[0] = '-'; | ||
66 | /* Delete old one, create new as a hardlink to current */ | ||
67 | i = (unlink(new_name) && errno != ENOENT); | ||
68 | if (i || link(filename, new_name)) | ||
69 | bb_perror_msg("warning: cannot create backup copy '%s'", new_name); | ||
70 | last_char[0] = '+'; | ||
71 | |||
72 | /* Lock the password file before updating */ | ||
73 | lock.l_type = F_WRLCK; | ||
74 | lock.l_whence = SEEK_SET; | ||
75 | lock.l_start = 0; | ||
76 | lock.l_len = 0; | ||
77 | if (fcntl(old_fd, F_SETLK, &lock) < 0) | ||
78 | bb_perror_msg("warning: cannot lock '%s'", filename); | ||
79 | lock.l_type = F_UNLCK; | ||
80 | |||
81 | /* Read current password file, write updated one */ | ||
82 | while (1) { | ||
83 | char *line = xmalloc_fgets(old_fp); | ||
84 | if (!line) break; /* EOF/error */ | ||
85 | if (strncmp(username, line, user_len) == 0) { | ||
86 | /* we have a match with "username:"... */ | ||
87 | const char *cp = line + user_len; | ||
88 | /* now cp -> old passwd, skip it: */ | ||
89 | cp = strchr(cp, ':'); | ||
90 | if (!cp) cp = ""; | ||
91 | /* now cp -> ':' after old passwd or -> "" */ | ||
92 | fprintf(new_fp, "%s%s%s", username, new_pw, cp); | ||
93 | cnt++; | ||
94 | } else | ||
95 | fputs(line, new_fp); | ||
96 | free(line); | ||
97 | } | ||
98 | fcntl(old_fd, F_SETLK, &lock); | ||
99 | |||
100 | /* We do want all of them to execute, thus | instead of || */ | ||
101 | if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) | ||
102 | || rename(new_name, filename) | ||
103 | ) { | ||
104 | /* At least one of those failed */ | ||
105 | goto unlink_new; | ||
106 | } | ||
107 | ret = cnt; /* whee, success! */ | ||
108 | |||
109 | unlink_new: | ||
110 | if (ret) unlink(new_name); | ||
111 | |||
112 | close_old_fp: | ||
113 | fclose(old_fp); | ||
114 | |||
115 | free_mem: | ||
116 | if (ENABLE_FEATURE_CLEAN_UP) free(new_name); | ||
117 | if (ENABLE_FEATURE_CLEAN_UP) free((char*)username); | ||
118 | return ret; | ||
119 | } | ||
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c new file mode 100644 index 000000000..124fc86e2 --- /dev/null +++ b/loginutils/chpasswd.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * chpasswd.c | ||
4 | * | ||
5 | * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org> | ||
6 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
7 | */ | ||
8 | |||
9 | #include "libbb.h" | ||
10 | |||
11 | #if ENABLE_GETOPT_LONG | ||
12 | #include <getopt.h> | ||
13 | |||
14 | static const struct option chpasswd_opts[] = { | ||
15 | { "encrypted", no_argument, NULL, 'e' }, | ||
16 | { "md5", no_argument, NULL, 'm' }, | ||
17 | { NULL, 0, NULL, 0 } | ||
18 | }; | ||
19 | #endif | ||
20 | |||
21 | #define OPT_ENC 1 | ||
22 | #define OPT_MD5 2 | ||
23 | |||
24 | int chpasswd_main(int argc, char **argv); | ||
25 | int chpasswd_main(int argc, char **argv) | ||
26 | { | ||
27 | char *name, *pass; | ||
28 | char salt[sizeof("$N$XXXXXXXX")]; | ||
29 | int opt, rc; | ||
30 | int rnd = rnd; /* we *want* it to be non-initialized! */ | ||
31 | const char *pwfile = bb_path_passwd_file; | ||
32 | |||
33 | if (getuid() != 0) | ||
34 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | ||
35 | |||
36 | #if ENABLE_FEATURE_SHADOWPASSWDS | ||
37 | if (access(bb_path_shadow_file, F_OK) == 0) | ||
38 | pwfile = bb_path_shadow_file; | ||
39 | #endif | ||
40 | |||
41 | opt_complementary = "m--e"; | ||
42 | USE_GETOPT_LONG(applet_long_options = chpasswd_opts;) | ||
43 | opt = getopt32(argc, argv, "em"); | ||
44 | |||
45 | while ((name = xmalloc_getline(stdin)) != NULL) { | ||
46 | pass = strchr(name, ':'); | ||
47 | if (!pass) | ||
48 | bb_error_msg_and_die("missing new password"); | ||
49 | *pass++ = '\0'; | ||
50 | |||
51 | //if (!getpwnam(name)) | ||
52 | // bb_error_msg_and_die("unknown user %s", name); | ||
53 | |||
54 | if (!(opt & OPT_ENC)) { | ||
55 | rnd = crypt_make_salt(salt, 1, rnd); | ||
56 | if (opt & OPT_MD5) { | ||
57 | strcpy(salt, "$1$"); | ||
58 | rnd = crypt_make_salt(salt + 3, 4, rnd); | ||
59 | } | ||
60 | pass = pw_encrypt(pass, salt); | ||
61 | } | ||
62 | |||
63 | rc = update_passwd(pwfile, name, pass); | ||
64 | /* LOGMODE_BOTH logs to syslog */ | ||
65 | logmode = LOGMODE_BOTH; | ||
66 | if (rc < 0) | ||
67 | bb_error_msg_and_die("an error occurred updating %s", pwfile); | ||
68 | if (rc > 0) | ||
69 | bb_info_msg("Password for '%s' changed", name); | ||
70 | else | ||
71 | bb_info_msg("User '%s' not found", name); | ||
72 | logmode = LOGMODE_STDIO; | ||
73 | free(name); | ||
74 | } | ||
75 | |||
76 | return 0; | ||
77 | } | ||