]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
f2d18633 | 2 | * "$Id$" |
ef416fc2 | 3 | * |
f8b3a85b | 4 | * MD5 password program for CUPS. |
ef416fc2 | 5 | * |
85dda01c | 6 | * Copyright 2007-2011 by Apple Inc. |
ef416fc2 | 7 | * Copyright 1997-2006 by Easy Software Products. |
8 | * | |
9 | * These coded instructions, statements, and computer programs are the | |
bc44d920 | 10 | * property of Apple Inc. and are protected by Federal copyright |
11 | * law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
12 | * which should have been included with this file. If this file is | |
13 | * file is missing or damaged, see the license at "http://www.cups.org/". | |
ef416fc2 | 14 | * |
15 | * Contents: | |
16 | * | |
17 | * main() - Add, change, or delete passwords from the MD5 password file. | |
18 | * usage() - Show program usage. | |
19 | */ | |
20 | ||
21 | /* | |
22 | * Include necessary headers... | |
23 | */ | |
24 | ||
71e16022 MS |
25 | #include <cups/cups-private.h> |
26 | #include <cups/md5-private.h> | |
ef416fc2 | 27 | #include <fcntl.h> |
28 | #include <grp.h> | |
29 | #include <sys/types.h> | |
30 | #include <sys/stat.h> | |
31 | ||
ef416fc2 | 32 | #ifndef WIN32 |
33 | # include <unistd.h> | |
34 | # include <signal.h> | |
35 | #endif /* !WIN32 */ | |
36 | ||
37 | ||
38 | /* | |
39 | * Operations... | |
40 | */ | |
41 | ||
42 | #define ADD 0 | |
43 | #define CHANGE 1 | |
44 | #define DELETE 2 | |
45 | ||
46 | ||
47 | /* | |
48 | * Local functions... | |
49 | */ | |
50 | ||
85dda01c | 51 | static void usage(FILE *fp) __attribute__((noreturn)); |
ef416fc2 | 52 | |
53 | ||
54 | /* | |
55 | * 'main()' - Add, change, or delete passwords from the MD5 password file. | |
56 | */ | |
57 | ||
58 | int /* O - Exit status */ | |
59 | main(int argc, /* I - Number of command-line arguments */ | |
60 | char *argv[]) /* I - Command-line arguments */ | |
61 | { | |
62 | int i; /* Looping var */ | |
63 | char *opt; /* Option pointer */ | |
64 | const char *username; /* Pointer to username */ | |
65 | const char *groupname; /* Pointer to group name */ | |
66 | int op; /* Operation (add, change, delete) */ | |
67 | const char *passwd; /* Password string */ | |
68 | FILE *infile, /* Input file */ | |
69 | *outfile; /* Output file */ | |
70 | char line[256], /* Line from file */ | |
71 | userline[17], /* User from line */ | |
72 | groupline[17], /* Group from line */ | |
73 | md5line[33], /* MD5-sum from line */ | |
74 | md5new[33]; /* New MD5 sum */ | |
ef416fc2 | 75 | char passwdmd5[1024], /* passwd.md5 file */ |
76 | passwdold[1024], /* passwd.old file */ | |
77 | passwdnew[1024]; /* passwd.tmp file */ | |
78 | char *newpass, /* new password */ | |
79 | *oldpass; /* old password */ | |
80 | int flag; /* Password check flags... */ | |
81 | int fd; /* Password file descriptor */ | |
82 | int error; /* Write error */ | |
f8b3a85b MS |
83 | _cups_globals_t *cg = _cupsGlobals(); /* Global data */ |
84 | cups_lang_t *lang; /* Language info */ | |
ef416fc2 | 85 | #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) |
86 | struct sigaction action; /* Signal action */ | |
87 | #endif /* HAVE_SIGACTION && !HAVE_SIGSET*/ | |
88 | ||
89 | ||
07725fee | 90 | _cupsSetLocale(argv); |
f8b3a85b | 91 | lang = cupsLangDefault(); |
d09495fa | 92 | |
ef416fc2 | 93 | /* |
94 | * Check to see if stdin, stdout, and stderr are still open... | |
95 | */ | |
96 | ||
97 | if (fcntl(0, F_GETFD, &i) || | |
98 | fcntl(1, F_GETFD, &i) || | |
99 | fcntl(2, F_GETFD, &i)) | |
100 | { | |
101 | /* | |
102 | * No, return exit status 2 and don't try to send any output since | |
103 | * someone is trying to bypass the security on the server. | |
104 | */ | |
105 | ||
106 | return (2); | |
107 | } | |
108 | ||
109 | /* | |
110 | * Find the server directory... | |
ef416fc2 | 111 | */ |
112 | ||
f8b3a85b MS |
113 | snprintf(passwdmd5, sizeof(passwdmd5), "%s/passwd.md5", cg->cups_serverroot); |
114 | snprintf(passwdold, sizeof(passwdold), "%s/passwd.old", cg->cups_serverroot); | |
115 | snprintf(passwdnew, sizeof(passwdnew), "%s/passwd.new", cg->cups_serverroot); | |
ef416fc2 | 116 | |
117 | /* | |
118 | * Find the default system group... | |
119 | */ | |
120 | ||
121 | if (getgrnam(CUPS_DEFAULT_GROUP)) | |
122 | groupname = CUPS_DEFAULT_GROUP; | |
123 | else | |
124 | groupname = "unknown"; | |
125 | ||
126 | endgrent(); | |
127 | ||
128 | username = NULL; | |
129 | op = CHANGE; | |
130 | ||
131 | /* | |
132 | * Parse command-line options... | |
133 | */ | |
134 | ||
135 | for (i = 1; i < argc; i ++) | |
136 | if (argv[i][0] == '-') | |
137 | for (opt = argv[i] + 1; *opt; opt ++) | |
138 | switch (*opt) | |
139 | { | |
140 | case 'a' : /* Add */ | |
141 | op = ADD; | |
142 | break; | |
143 | case 'x' : /* Delete */ | |
144 | op = DELETE; | |
145 | break; | |
146 | case 'g' : /* Group */ | |
147 | i ++; | |
148 | if (i >= argc) | |
149 | usage(stderr); | |
150 | ||
151 | groupname = argv[i]; | |
152 | break; | |
153 | case 'h' : /* Help */ | |
154 | usage(stdout); | |
155 | break; | |
156 | default : /* Bad option */ | |
157 | usage(stderr); | |
158 | break; | |
159 | } | |
160 | else if (!username) | |
161 | username = argv[i]; | |
162 | else | |
163 | usage(stderr); | |
164 | ||
165 | /* | |
166 | * See if we are trying to add or delete a password when we aren't logged in | |
167 | * as root... | |
168 | */ | |
169 | ||
170 | if (getuid() && getuid() != geteuid() && (op != CHANGE || username)) | |
171 | { | |
fa73b229 | 172 | _cupsLangPuts(stderr, |
0837b7e8 | 173 | _("lppasswd: Only root can add or delete passwords.")); |
ef416fc2 | 174 | return (1); |
175 | } | |
176 | ||
177 | /* | |
178 | * Fill in missing info... | |
179 | */ | |
180 | ||
181 | if (!username) | |
182 | username = cupsUser(); | |
183 | ||
184 | oldpass = newpass = NULL; | |
185 | ||
186 | /* | |
187 | * Obtain old and new password _before_ locking the database | |
188 | * to keep users from locking the file indefinitely. | |
189 | */ | |
190 | ||
191 | if (op == CHANGE && getuid()) | |
192 | { | |
193 | if ((passwd = cupsGetPassword(_("Enter old password:"))) == NULL) | |
194 | return (1); | |
195 | ||
196 | if ((oldpass = strdup(passwd)) == NULL) | |
197 | { | |
fa73b229 | 198 | _cupsLangPrintf(stderr, |
0837b7e8 | 199 | _("lppasswd: Unable to copy password string: %s"), |
ef416fc2 | 200 | strerror(errno)); |
201 | return (1); | |
202 | } | |
203 | } | |
204 | ||
205 | /* | |
206 | * Now get the new password, if necessary... | |
207 | */ | |
208 | ||
209 | if (op != DELETE) | |
210 | { | |
f8b3a85b MS |
211 | if ((passwd = cupsGetPassword( |
212 | _cupsLangString(lang, _("Enter password:")))) == NULL) | |
ef416fc2 | 213 | return (1); |
214 | ||
215 | if ((newpass = strdup(passwd)) == NULL) | |
216 | { | |
fa73b229 | 217 | _cupsLangPrintf(stderr, |
0837b7e8 | 218 | _("lppasswd: Unable to copy password string: %s"), |
ef416fc2 | 219 | strerror(errno)); |
220 | return (1); | |
221 | } | |
222 | ||
f8b3a85b MS |
223 | if ((passwd = cupsGetPassword( |
224 | _cupsLangString(lang, _("Enter password again:")))) == NULL) | |
ef416fc2 | 225 | return (1); |
226 | ||
227 | if (strcmp(passwd, newpass) != 0) | |
228 | { | |
fa73b229 | 229 | _cupsLangPuts(stderr, |
0837b7e8 | 230 | _("lppasswd: Sorry, passwords don't match.")); |
ef416fc2 | 231 | return (1); |
232 | } | |
233 | ||
234 | /* | |
235 | * Check that the password contains at least one letter and number. | |
236 | */ | |
237 | ||
238 | flag = 0; | |
239 | ||
240 | for (passwd = newpass; *passwd; passwd ++) | |
241 | if (isdigit(*passwd & 255)) | |
242 | flag |= 1; | |
243 | else if (isalpha(*passwd & 255)) | |
244 | flag |= 2; | |
245 | ||
246 | /* | |
247 | * Only allow passwords that are at least 6 chars, have a letter and | |
248 | * a number, and don't contain the username. | |
249 | */ | |
250 | ||
251 | if (strlen(newpass) < 6 || strstr(newpass, username) != NULL || flag != 3) | |
252 | { | |
0837b7e8 MS |
253 | _cupsLangPuts(stderr, _("lppasswd: Sorry, password rejected.")); |
254 | _cupsLangPuts(stderr, _("Your password must be at least 6 characters " | |
255 | "long, cannot contain your username, and must " | |
256 | "contain at least one letter and number.")); | |
ef416fc2 | 257 | return (1); |
258 | } | |
259 | } | |
260 | ||
261 | /* | |
262 | * Ignore SIGHUP, SIGINT, SIGTERM, and SIGXFSZ (if defined) for the | |
263 | * remainder of the time so that we won't end up with bogus password | |
264 | * files... | |
265 | */ | |
266 | ||
267 | #ifndef WIN32 | |
268 | # if defined(HAVE_SIGSET) | |
269 | sigset(SIGHUP, SIG_IGN); | |
270 | sigset(SIGINT, SIG_IGN); | |
271 | sigset(SIGTERM, SIG_IGN); | |
272 | # ifdef SIGXFSZ | |
273 | sigset(SIGXFSZ, SIG_IGN); | |
274 | # endif /* SIGXFSZ */ | |
275 | # elif defined(HAVE_SIGACTION) | |
276 | memset(&action, 0, sizeof(action)); | |
277 | action.sa_handler = SIG_IGN; | |
278 | ||
279 | sigaction(SIGHUP, &action, NULL); | |
280 | sigaction(SIGINT, &action, NULL); | |
281 | sigaction(SIGTERM, &action, NULL); | |
282 | # ifdef SIGXFSZ | |
283 | sigaction(SIGXFSZ, &action, NULL); | |
284 | # endif /* SIGXFSZ */ | |
285 | # else | |
286 | signal(SIGHUP, SIG_IGN); | |
287 | signal(SIGINT, SIG_IGN); | |
288 | signal(SIGTERM, SIG_IGN); | |
289 | # ifdef SIGXFSZ | |
290 | signal(SIGXFSZ, SIG_IGN); | |
291 | # endif /* SIGXFSZ */ | |
292 | # endif | |
293 | #endif /* !WIN32 */ | |
294 | ||
295 | /* | |
296 | * Open the output file. | |
297 | */ | |
298 | ||
299 | if ((fd = open(passwdnew, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0) | |
300 | { | |
301 | if (errno == EEXIST) | |
0837b7e8 | 302 | _cupsLangPuts(stderr, _("lppasswd: Password file busy.")); |
ef416fc2 | 303 | else |
0837b7e8 | 304 | _cupsLangPrintf(stderr, _("lppasswd: Unable to open password file: %s"), |
ef416fc2 | 305 | strerror(errno)); |
306 | ||
307 | return (1); | |
308 | } | |
309 | ||
310 | if ((outfile = fdopen(fd, "w")) == NULL) | |
311 | { | |
0837b7e8 | 312 | _cupsLangPrintf(stderr, _("lppasswd: Unable to open password file: %s"), |
ef416fc2 | 313 | strerror(errno)); |
314 | ||
315 | unlink(passwdnew); | |
316 | ||
317 | return (1); | |
318 | } | |
319 | ||
320 | setbuf(outfile, NULL); | |
321 | ||
322 | /* | |
323 | * Open the existing password file and create a new one... | |
324 | */ | |
325 | ||
326 | infile = fopen(passwdmd5, "r"); | |
327 | if (infile == NULL && errno != ENOENT && op != ADD) | |
328 | { | |
0837b7e8 | 329 | _cupsLangPrintf(stderr, _("lppasswd: Unable to open password file: %s"), |
ef416fc2 | 330 | strerror(errno)); |
331 | ||
332 | fclose(outfile); | |
333 | ||
334 | unlink(passwdnew); | |
335 | ||
336 | return (1); | |
337 | } | |
338 | ||
339 | /* | |
340 | * Read lines from the password file; the format is: | |
341 | * | |
342 | * username:group:MD5-sum | |
343 | */ | |
344 | ||
345 | error = 0; | |
346 | userline[0] = '\0'; | |
347 | groupline[0] = '\0'; | |
348 | md5line[0] = '\0'; | |
349 | ||
350 | if (infile) | |
351 | { | |
352 | while (fgets(line, sizeof(line), infile) != NULL) | |
353 | { | |
354 | if (sscanf(line, "%16[^:]:%16[^:]:%32s", userline, groupline, md5line) != 3) | |
355 | continue; | |
356 | ||
357 | if (strcmp(username, userline) == 0 && | |
358 | strcmp(groupname, groupline) == 0) | |
359 | break; | |
360 | ||
361 | if (fputs(line, outfile) == EOF) | |
362 | { | |
fa73b229 | 363 | _cupsLangPrintf(stderr, |
0837b7e8 | 364 | _("lppasswd: Unable to write to password file: %s"), |
ef416fc2 | 365 | strerror(errno)); |
366 | error = 1; | |
367 | break; | |
368 | } | |
369 | } | |
370 | ||
371 | if (!error) | |
372 | { | |
373 | while (fgets(line, sizeof(line), infile) != NULL) | |
374 | if (fputs(line, outfile) == EOF) | |
375 | { | |
fa73b229 | 376 | _cupsLangPrintf(stderr, |
0837b7e8 | 377 | _("lppasswd: Unable to write to password file: %s"), |
ef416fc2 | 378 | strerror(errno)); |
379 | error = 1; | |
380 | break; | |
381 | } | |
382 | } | |
383 | } | |
384 | ||
385 | if (op == CHANGE && | |
386 | (strcmp(username, userline) || strcmp(groupname, groupline))) | |
387 | { | |
fa73b229 | 388 | _cupsLangPrintf(stderr, |
0837b7e8 | 389 | _("lppasswd: user \"%s\" and group \"%s\" do not exist."), |
ef416fc2 | 390 | username, groupname); |
391 | error = 1; | |
392 | } | |
393 | else if (op != DELETE) | |
394 | { | |
395 | if (oldpass && | |
396 | strcmp(httpMD5(username, "CUPS", oldpass, md5new), md5line) != 0) | |
397 | { | |
0837b7e8 | 398 | _cupsLangPuts(stderr, _("lppasswd: Sorry, password doesn't match.")); |
ef416fc2 | 399 | error = 1; |
400 | } | |
401 | else | |
402 | { | |
403 | snprintf(line, sizeof(line), "%s:%s:%s\n", username, groupname, | |
404 | httpMD5(username, "CUPS", newpass, md5new)); | |
405 | if (fputs(line, outfile) == EOF) | |
406 | { | |
fa73b229 | 407 | _cupsLangPrintf(stderr, |
0837b7e8 | 408 | _("lppasswd: Unable to write to password file: %s"), |
ef416fc2 | 409 | strerror(errno)); |
410 | error = 1; | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
415 | /* | |
416 | * Close the files... | |
417 | */ | |
418 | ||
419 | if (infile) | |
420 | fclose(infile); | |
421 | ||
422 | if (fclose(outfile) == EOF) | |
423 | error = 1; | |
424 | ||
425 | /* | |
426 | * Error out gracefully as needed... | |
427 | */ | |
428 | ||
429 | if (error) | |
430 | { | |
0837b7e8 | 431 | _cupsLangPuts(stderr, _("lppasswd: Password file not updated.")); |
85dda01c | 432 | |
ef416fc2 | 433 | unlink(passwdnew); |
434 | ||
435 | return (1); | |
436 | } | |
437 | ||
438 | /* | |
439 | * Save old passwd file | |
440 | */ | |
441 | ||
442 | unlink(passwdold); | |
443 | if (link(passwdmd5, passwdold) && errno != ENOENT) | |
444 | { | |
fa73b229 | 445 | _cupsLangPrintf(stderr, |
0837b7e8 | 446 | _("lppasswd: failed to backup old password file: %s"), |
ef416fc2 | 447 | strerror(errno)); |
448 | unlink(passwdnew); | |
449 | return (1); | |
450 | } | |
451 | ||
452 | /* | |
453 | * Install new password file | |
454 | */ | |
455 | ||
456 | if (rename(passwdnew, passwdmd5) < 0) | |
457 | { | |
0837b7e8 | 458 | _cupsLangPrintf(stderr, _("lppasswd: failed to rename password file: %s"), |
ef416fc2 | 459 | strerror(errno)); |
460 | unlink(passwdnew); | |
461 | return (1); | |
462 | } | |
463 | ||
464 | return (0); | |
465 | } | |
466 | ||
467 | ||
468 | /* | |
469 | * 'usage()' - Show program usage. | |
470 | */ | |
471 | ||
472 | static void | |
473 | usage(FILE *fp) /* I - File to send usage to */ | |
474 | { | |
475 | if (getuid()) | |
0837b7e8 | 476 | _cupsLangPuts(fp, _("Usage: lppasswd [-g groupname]")); |
ef416fc2 | 477 | else |
85dda01c | 478 | _cupsLangPuts(fp, |
ef416fc2 | 479 | _("Usage: lppasswd [-g groupname] [username]\n" |
480 | " lppasswd [-g groupname] -a [username]\n" | |
0837b7e8 | 481 | " lppasswd [-g groupname] -x [username]")); |
ef416fc2 | 482 | |
483 | exit(1); | |
484 | } | |
485 | ||
486 | ||
487 | /* | |
f2d18633 | 488 | * End of "$Id$". |
ef416fc2 | 489 | */ |