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