]> git.ipfire.org Git - thirdparty/cups.git/blob - systemv/lppasswd.c
Merge changes from CUPS 1.5svn-r9049 (private header support)
[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 <cups/cups-private.h>
26 #include <cups/md5-private.h>
27 #include <fcntl.h>
28 #include <grp.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
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
51 static void usage(FILE *fp);
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 */
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 */
83 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
84 cups_lang_t *lang; /* Language info */
85 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
86 struct sigaction action; /* Signal action */
87 #endif /* HAVE_SIGACTION && !HAVE_SIGSET*/
88
89
90 _cupsSetLocale(argv);
91 lang = cupsLangDefault();
92
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...
111 */
112
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);
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 {
172 _cupsLangPuts(stderr,
173 _("lppasswd: Only root can add or delete passwords\n"));
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 {
198 _cupsLangPrintf(stderr,
199 _("lppasswd: Unable to copy password string: %s\n"),
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 {
211 if ((passwd = cupsGetPassword(
212 _cupsLangString(lang, _("Enter password:")))) == NULL)
213 return (1);
214
215 if ((newpass = strdup(passwd)) == NULL)
216 {
217 _cupsLangPrintf(stderr,
218 _("lppasswd: Unable to copy password string: %s\n"),
219 strerror(errno));
220 return (1);
221 }
222
223 if ((passwd = cupsGetPassword(
224 _cupsLangString(lang, _("Enter password again:")))) == NULL)
225 return (1);
226
227 if (strcmp(passwd, newpass) != 0)
228 {
229 _cupsLangPuts(stderr,
230 _("lppasswd: Sorry, passwords don't match\n"));
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 {
253 _cupsLangPuts(stderr,
254 _("lppasswd: Sorry, password rejected.\n"
255 "Your password must be at least 6 characters long, "
256 "cannot contain\n"
257 "your username, and must contain at least one letter "
258 "and number.\n"));
259 return (1);
260 }
261 }
262
263 /*
264 * Ignore SIGHUP, SIGINT, SIGTERM, and SIGXFSZ (if defined) for the
265 * remainder of the time so that we won't end up with bogus password
266 * files...
267 */
268
269 #ifndef WIN32
270 # if defined(HAVE_SIGSET)
271 sigset(SIGHUP, SIG_IGN);
272 sigset(SIGINT, SIG_IGN);
273 sigset(SIGTERM, SIG_IGN);
274 # ifdef SIGXFSZ
275 sigset(SIGXFSZ, SIG_IGN);
276 # endif /* SIGXFSZ */
277 # elif defined(HAVE_SIGACTION)
278 memset(&action, 0, sizeof(action));
279 action.sa_handler = SIG_IGN;
280
281 sigaction(SIGHUP, &action, NULL);
282 sigaction(SIGINT, &action, NULL);
283 sigaction(SIGTERM, &action, NULL);
284 # ifdef SIGXFSZ
285 sigaction(SIGXFSZ, &action, NULL);
286 # endif /* SIGXFSZ */
287 # else
288 signal(SIGHUP, SIG_IGN);
289 signal(SIGINT, SIG_IGN);
290 signal(SIGTERM, SIG_IGN);
291 # ifdef SIGXFSZ
292 signal(SIGXFSZ, SIG_IGN);
293 # endif /* SIGXFSZ */
294 # endif
295 #endif /* !WIN32 */
296
297 /*
298 * Open the output file.
299 */
300
301 if ((fd = open(passwdnew, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0)
302 {
303 if (errno == EEXIST)
304 _cupsLangPuts(stderr, _("lppasswd: Password file busy\n"));
305 else
306 _cupsLangPrintf(stderr,
307 _("lppasswd: Unable to open password file: %s\n"),
308 strerror(errno));
309
310 return (1);
311 }
312
313 if ((outfile = fdopen(fd, "w")) == NULL)
314 {
315 _cupsLangPrintf(stderr,
316 _("lppasswd: Unable to open password file: %s\n"),
317 strerror(errno));
318
319 unlink(passwdnew);
320
321 return (1);
322 }
323
324 setbuf(outfile, NULL);
325
326 /*
327 * Open the existing password file and create a new one...
328 */
329
330 infile = fopen(passwdmd5, "r");
331 if (infile == NULL && errno != ENOENT && op != ADD)
332 {
333 _cupsLangPrintf(stderr,
334 _("lppasswd: Unable to open password file: %s\n"),
335 strerror(errno));
336
337 fclose(outfile);
338
339 unlink(passwdnew);
340
341 return (1);
342 }
343
344 /*
345 * Read lines from the password file; the format is:
346 *
347 * username:group:MD5-sum
348 */
349
350 error = 0;
351 userline[0] = '\0';
352 groupline[0] = '\0';
353 md5line[0] = '\0';
354
355 if (infile)
356 {
357 while (fgets(line, sizeof(line), infile) != NULL)
358 {
359 if (sscanf(line, "%16[^:]:%16[^:]:%32s", userline, groupline, md5line) != 3)
360 continue;
361
362 if (strcmp(username, userline) == 0 &&
363 strcmp(groupname, groupline) == 0)
364 break;
365
366 if (fputs(line, outfile) == EOF)
367 {
368 _cupsLangPrintf(stderr,
369 _("lppasswd: Unable to write to password file: %s\n"),
370 strerror(errno));
371 error = 1;
372 break;
373 }
374 }
375
376 if (!error)
377 {
378 while (fgets(line, sizeof(line), infile) != NULL)
379 if (fputs(line, outfile) == EOF)
380 {
381 _cupsLangPrintf(stderr,
382 _("lppasswd: Unable to write to password file: %s\n"),
383 strerror(errno));
384 error = 1;
385 break;
386 }
387 }
388 }
389
390 if (op == CHANGE &&
391 (strcmp(username, userline) || strcmp(groupname, groupline)))
392 {
393 _cupsLangPrintf(stderr,
394 _("lppasswd: user \"%s\" and group \"%s\" do not exist.\n"),
395 username, groupname);
396 error = 1;
397 }
398 else if (op != DELETE)
399 {
400 if (oldpass &&
401 strcmp(httpMD5(username, "CUPS", oldpass, md5new), md5line) != 0)
402 {
403 _cupsLangPuts(stderr,
404 _("lppasswd: Sorry, password doesn't match\n"));
405 error = 1;
406 }
407 else
408 {
409 snprintf(line, sizeof(line), "%s:%s:%s\n", username, groupname,
410 httpMD5(username, "CUPS", newpass, md5new));
411 if (fputs(line, outfile) == EOF)
412 {
413 _cupsLangPrintf(stderr,
414 _("lppasswd: Unable to write to password file: %s\n"),
415 strerror(errno));
416 error = 1;
417 }
418 }
419 }
420
421 /*
422 * Close the files...
423 */
424
425 if (infile)
426 fclose(infile);
427
428 if (fclose(outfile) == EOF)
429 error = 1;
430
431 /*
432 * Error out gracefully as needed...
433 */
434
435 if (error)
436 {
437 _cupsLangPuts(stderr, _("lppasswd: Password file not updated\n"));
438
439 unlink(passwdnew);
440
441 return (1);
442 }
443
444 /*
445 * Save old passwd file
446 */
447
448 unlink(passwdold);
449 if (link(passwdmd5, passwdold) && errno != ENOENT)
450 {
451 _cupsLangPrintf(stderr,
452 _("lppasswd: failed to backup old password file: %s\n"),
453 strerror(errno));
454 unlink(passwdnew);
455 return (1);
456 }
457
458 /*
459 * Install new password file
460 */
461
462 if (rename(passwdnew, passwdmd5) < 0)
463 {
464 _cupsLangPrintf(stderr,
465 _("lppasswd: failed to rename password file: %s\n"),
466 strerror(errno));
467 unlink(passwdnew);
468 return (1);
469 }
470
471 return (0);
472 }
473
474
475 /*
476 * 'usage()' - Show program usage.
477 */
478
479 static void
480 usage(FILE *fp) /* I - File to send usage to */
481 {
482 if (getuid())
483 _cupsLangPuts(fp, _("Usage: lppasswd [-g groupname]\n"));
484 else
485 _cupsLangPuts(fp,
486 _("Usage: lppasswd [-g groupname] [username]\n"
487 " lppasswd [-g groupname] -a [username]\n"
488 " lppasswd [-g groupname] -x [username]\n"));
489
490 exit(1);
491 }
492
493
494 /*
495 * End of "$Id: lppasswd.c 6649 2007-07-11 21:46:42Z mike $".
496 */