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