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