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