Contents of /trunk/mkinitrd-magellan/busybox/loginutils/passwd.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 8561 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 8561 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
4 | */ |
5 | |
6 | #include "busybox.h" |
7 | #include <syslog.h> |
8 | |
9 | |
10 | static void nuke_str(char *str) |
11 | { |
12 | if (str) memset(str, 0, strlen(str)); |
13 | } |
14 | |
15 | |
16 | static int i64c(int i) |
17 | { |
18 | i &= 0x3f; |
19 | if (i == 0) |
20 | return '.'; |
21 | if (i == 1) |
22 | return '/'; |
23 | if (i < 12) |
24 | return ('0' - 2 + i); |
25 | if (i < 38) |
26 | return ('A' - 12 + i); |
27 | return ('a' - 38 + i); |
28 | } |
29 | |
30 | |
31 | static void crypt_make_salt(char *p, int cnt) |
32 | { |
33 | unsigned x = x; /* it's pointless to initialize it anyway :) */ |
34 | |
35 | x += getpid() + time(NULL) + clock(); |
36 | do { |
37 | /* x = (x*1664525 + 1013904223) % 2^32 generator is lame |
38 | * (low-order bit is not "random", etc...), |
39 | * but for our purposes it is good enough */ |
40 | x = x*1664525 + 1013904223; |
41 | /* BTW, Park and Miller's "minimal standard generator" is |
42 | * x = x*16807 % ((2^31)-1) |
43 | * It has no problem with visibly alternating lowest bit |
44 | * but is also weak in cryptographic sense + needs div, |
45 | * which needs more code (and slower) on many CPUs */ |
46 | *p++ = i64c(x >> 16); |
47 | *p++ = i64c(x >> 22); |
48 | } while (--cnt); |
49 | *p = '\0'; |
50 | } |
51 | |
52 | |
53 | static char* new_password(const struct passwd *pw, uid_t myuid, int algo) |
54 | { |
55 | char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */ |
56 | char *orig = ""; |
57 | char *newp = NULL; |
58 | char *cipher = NULL; |
59 | char *cp = NULL; |
60 | char *ret = NULL; /* failure so far */ |
61 | |
62 | if (myuid && pw->pw_passwd[0]) { |
63 | orig = bb_askpass(0, "Old password:"); /* returns ptr to static */ |
64 | if (!orig) |
65 | goto err_ret; |
66 | cipher = pw_encrypt(orig, pw->pw_passwd); /* returns ptr to static */ |
67 | if (strcmp(cipher, pw->pw_passwd) != 0) { |
68 | syslog(LOG_WARNING, "incorrect password for '%s'", |
69 | pw->pw_name); |
70 | bb_do_delay(FAIL_DELAY); |
71 | puts("Incorrect password"); |
72 | goto err_ret; |
73 | } |
74 | } |
75 | orig = xstrdup(orig); /* or else bb_askpass() will destroy it */ |
76 | newp = bb_askpass(0, "New password:"); /* returns ptr to static */ |
77 | if (!newp) |
78 | goto err_ret; |
79 | newp = xstrdup(newp); /* we are going to bb_askpass() again, so save it */ |
80 | if (ENABLE_FEATURE_PASSWD_WEAK_CHECK |
81 | && obscure(orig, newp, pw) && myuid) |
82 | goto err_ret; /* non-root is not allowed to have weak passwd */ |
83 | |
84 | cp = bb_askpass(0, "Retype password:"); |
85 | if (!cp) |
86 | goto err_ret; |
87 | if (strcmp(cp, newp)) { |
88 | puts("Passwords don't match"); |
89 | goto err_ret; |
90 | } |
91 | |
92 | /*memset(salt, 0, sizeof(salt)); - why?*/ |
93 | crypt_make_salt(salt, 1); /* des */ |
94 | if (algo) { /* MD5 */ |
95 | strcpy(salt, "$1$"); |
96 | crypt_make_salt(salt + 3, 4); |
97 | } |
98 | ret = xstrdup(pw_encrypt(newp, salt)); /* returns ptr to static */ |
99 | /* whee, success! */ |
100 | |
101 | err_ret: |
102 | nuke_str(orig); |
103 | if (ENABLE_FEATURE_CLEAN_UP) free(orig); |
104 | nuke_str(newp); |
105 | if (ENABLE_FEATURE_CLEAN_UP) free(newp); |
106 | nuke_str(cipher); |
107 | nuke_str(cp); |
108 | return ret; |
109 | } |
110 | |
111 | |
112 | #if 0 |
113 | static int get_algo(char *a) |
114 | { |
115 | /* standard: MD5 */ |
116 | int x = 1; |
117 | if (strcasecmp(a, "des") == 0) |
118 | x = 0; |
119 | return x; |
120 | } |
121 | #endif |
122 | |
123 | |
124 | static int update_passwd(const char *filename, const char *username, |
125 | const char *new_pw) |
126 | { |
127 | struct stat sb; |
128 | struct flock lock; |
129 | FILE *old_fp; |
130 | FILE *new_fp; |
131 | char *new_name; |
132 | char *last_char; |
133 | unsigned user_len; |
134 | int old_fd; |
135 | int new_fd; |
136 | int i; |
137 | int ret = 1; /* failure */ |
138 | |
139 | logmode = LOGMODE_STDIO; |
140 | /* New passwd file, "/etc/passwd+" for now */ |
141 | new_name = xasprintf("%s+", filename); |
142 | last_char = &new_name[strlen(new_name)-1]; |
143 | username = xasprintf("%s:", username); |
144 | user_len = strlen(username); |
145 | |
146 | old_fp = fopen(filename, "r+"); |
147 | if (!old_fp) |
148 | goto free_mem; |
149 | old_fd = fileno(old_fp); |
150 | |
151 | /* Try to create "/etc/passwd+". Wait if it exists. */ |
152 | i = 30; |
153 | do { |
154 | // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC? |
155 | new_fd = open(new_name, O_WRONLY|O_CREAT|O_EXCL,0600); |
156 | if (new_fd >= 0) goto created; |
157 | if (errno != EEXIST) break; |
158 | usleep(100000); /* 0.1 sec */ |
159 | } while (--i); |
160 | bb_perror_msg("cannot create '%s'", new_name); |
161 | goto close_old_fp; |
162 | created: |
163 | if (!fstat(old_fd, &sb)) { |
164 | fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ |
165 | fchown(new_fd, sb.st_uid, sb.st_gid); |
166 | } |
167 | new_fp = fdopen(new_fd, "w"); |
168 | if (!new_fp) { |
169 | close(new_fd); |
170 | goto unlink_new; |
171 | } |
172 | |
173 | /* Backup file is "/etc/passwd-" */ |
174 | last_char[0] = '-'; |
175 | /* Delete old one, create new as a hardlink to current */ |
176 | i = (unlink(new_name) && errno != ENOENT); |
177 | if (i || link(filename, new_name)) |
178 | bb_perror_msg("warning: cannot create backup copy '%s'", new_name); |
179 | last_char[0] = '+'; |
180 | |
181 | /* Lock the password file before updating */ |
182 | lock.l_type = F_WRLCK; |
183 | lock.l_whence = SEEK_SET; |
184 | lock.l_start = 0; |
185 | lock.l_len = 0; |
186 | if (fcntl(old_fd, F_SETLK, &lock) < 0) |
187 | bb_perror_msg("warning: cannot lock '%s'", filename); |
188 | lock.l_type = F_UNLCK; |
189 | |
190 | /* Read current password file, write updated one */ |
191 | while (1) { |
192 | char *line = xmalloc_fgets(old_fp); |
193 | if (!line) break; /* EOF/error */ |
194 | if (strncmp(username, line, user_len) == 0) { |
195 | /* we have a match with "username:"... */ |
196 | const char *cp = line + user_len; |
197 | /* now cp -> old passwd, skip it: */ |
198 | cp = strchr(cp, ':'); |
199 | if (!cp) cp = ""; |
200 | /* now cp -> ':' after old passwd or -> "" */ |
201 | fprintf(new_fp, "%s%s%s", username, new_pw, cp); |
202 | /* Erase password in memory */ |
203 | } else |
204 | fputs(line, new_fp); |
205 | free(line); |
206 | } |
207 | fcntl(old_fd, F_SETLK, &lock); |
208 | |
209 | /* We do want all of them to execute, thus | instead of || */ |
210 | if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) |
211 | || rename(new_name, filename) |
212 | ) { |
213 | /* At least one of those failed */ |
214 | goto unlink_new; |
215 | } |
216 | ret = 0; /* whee, success! */ |
217 | |
218 | unlink_new: |
219 | if (ret) unlink(new_name); |
220 | |
221 | close_old_fp: |
222 | fclose(old_fp); |
223 | |
224 | free_mem: |
225 | if (ENABLE_FEATURE_CLEAN_UP) free(new_name); |
226 | if (ENABLE_FEATURE_CLEAN_UP) free((char*)username); |
227 | logmode = LOGMODE_BOTH; |
228 | return ret; |
229 | } |
230 | |
231 | |
232 | int passwd_main(int argc, char **argv) |
233 | { |
234 | enum { |
235 | OPT_algo = 0x1, /* -a - password algorithm */ |
236 | OPT_lock = 0x2, /* -l - lock account */ |
237 | OPT_unlock = 0x4, /* -u - unlock account */ |
238 | OPT_delete = 0x8, /* -d - delete password */ |
239 | OPT_lud = 0xe, |
240 | STATE_ALGO_md5 = 0x10, |
241 | /*STATE_ALGO_des = 0x20, not needed yet */ |
242 | }; |
243 | unsigned opt; |
244 | char *opt_a = ""; |
245 | const char *filename; |
246 | char *myname; |
247 | char *name; |
248 | char *newp; |
249 | struct passwd *pw; |
250 | uid_t myuid; |
251 | struct rlimit rlimit_fsize; |
252 | char c; |
253 | |
254 | logmode = LOGMODE_BOTH; |
255 | openlog(applet_name, LOG_NOWAIT, LOG_AUTH); |
256 | opt = getopt32(argc, argv, "a:lud", &opt_a); |
257 | argc -= optind; |
258 | argv += optind; |
259 | |
260 | if (strcasecmp(opt_a, "des") != 0) /* -a */ |
261 | opt |= STATE_ALGO_md5; |
262 | //else |
263 | // opt |= STATE_ALGO_des; |
264 | myuid = getuid(); |
265 | if ((opt & OPT_lud) && (!argc || myuid)) |
266 | bb_show_usage(); |
267 | |
268 | myname = xstrdup(bb_getpwuid(NULL, myuid, -1)); |
269 | name = argc ? argv[0] : myname; |
270 | |
271 | pw = getpwnam(name); |
272 | if (!pw) bb_error_msg_and_die("unknown user %s", name); |
273 | if (myuid && pw->pw_uid != myuid) { |
274 | /* LOGMODE_BOTH */ |
275 | bb_error_msg_and_die("%s can't change password for %s", myname, name); |
276 | } |
277 | |
278 | filename = bb_path_passwd_file; |
279 | #if ENABLE_FEATURE_SHADOWPASSWDS |
280 | { |
281 | struct spwd *sp = getspnam(name); |
282 | if (!sp) { |
283 | /* LOGMODE_BOTH */ |
284 | bb_error_msg("no record of %s in %s, using %s", |
285 | name, bb_path_shadow_file, |
286 | bb_path_passwd_file); |
287 | } else { |
288 | filename = bb_path_shadow_file; |
289 | pw->pw_passwd = sp->sp_pwdp; |
290 | } |
291 | } |
292 | #endif |
293 | |
294 | /* Decide what the new password will be */ |
295 | newp = NULL; |
296 | c = pw->pw_passwd[0] - '!'; |
297 | if (!(opt & OPT_lud)) { |
298 | if (myuid && !c) { /* passwd starts with '!' */ |
299 | /* LOGMODE_BOTH */ |
300 | bb_error_msg_and_die("cannot change " |
301 | "locked password for %s", name); |
302 | } |
303 | printf("Changing password for %s\n", name); |
304 | newp = new_password(pw, myuid, opt & STATE_ALGO_md5); |
305 | if (!newp) { |
306 | logmode = LOGMODE_STDIO; |
307 | bb_error_msg_and_die("password for %s is unchanged", name); |
308 | } |
309 | } else if (opt & OPT_lock) { |
310 | if (!c) goto skip; /* passwd starts with '!' */ |
311 | newp = xasprintf("!%s", pw->pw_passwd); |
312 | } else if (opt & OPT_unlock) { |
313 | if (c) goto skip; /* not '!' */ |
314 | newp = xstrdup(&pw->pw_passwd[1]); |
315 | } else if (opt & OPT_delete) { |
316 | newp = xstrdup(""); |
317 | } |
318 | |
319 | rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; |
320 | setrlimit(RLIMIT_FSIZE, &rlimit_fsize); |
321 | signal(SIGHUP, SIG_IGN); |
322 | signal(SIGINT, SIG_IGN); |
323 | signal(SIGQUIT, SIG_IGN); |
324 | umask(077); |
325 | xsetuid(0); |
326 | if (update_passwd(filename, name, newp) != 0) { |
327 | /* LOGMODE_BOTH */ |
328 | bb_error_msg_and_die("cannot update password file %s", |
329 | filename); |
330 | } |
331 | /* LOGMODE_BOTH */ |
332 | bb_info_msg("Password for %s changed by %s", name, myname); |
333 | |
334 | if (ENABLE_FEATURE_CLEAN_UP) free(newp); |
335 | skip: |
336 | if (!newp) { |
337 | bb_error_msg_and_die("password for %s is already %slocked", |
338 | name, (opt & OPT_unlock) ? "un" : ""); |
339 | } |
340 | if (ENABLE_FEATURE_CLEAN_UP) free(myname); |
341 | return 0; |
342 | } |