#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cups/cups.h>
#include <cups/md5.h>
#include <cups/string.h>
#ifndef WIN32
# include <unistd.h>
# include <signal.h>
#endif
#define ADD 0
#define CHANGE 1
#define DELETE 2
static void usage(FILE *fp);
int
main(int argc,
char *argv[])
{
int i;
char *opt;
const char *username;
const char *groupname;
int op;
const char *passwd;
FILE *infile,
*outfile;
char line[256],
userline[17],
groupline[17],
md5line[33],
md5new[33];
const char *root;
char passwdmd5[1024],
passwdold[1024],
passwdnew[1024];
char *newpass,
*oldpass;
int flag;
int fd;
int error;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
if (fcntl(0, F_GETFD, &i) ||
fcntl(1, F_GETFD, &i) ||
fcntl(2, F_GETFD, &i))
{
return (2);
}
if (!getuid() && (root = getenv("CUPS_SERVERROOT")) != NULL)
{
snprintf(passwdmd5, sizeof(passwdmd5), "%s/passwd.md5", root);
snprintf(passwdold, sizeof(passwdold), "%s/passwd.old", root);
snprintf(passwdnew, sizeof(passwdnew), "%s/passwd.new", root);
}
else
{
strcpy(passwdmd5, CUPS_SERVERROOT "/passwd.md5");
strcpy(passwdold, CUPS_SERVERROOT "/passwd.old");
strcpy(passwdnew, CUPS_SERVERROOT "/passwd.new");
}
if (getgrnam("sys"))
groupname = "sys";
else if (getgrnam("system"))
groupname = "system";
else if (getgrnam("root"))
groupname = "root";
else
groupname = "unknown";
endgrent();
username = NULL;
op = CHANGE;
for (i = 1; i < argc; i ++)
if (argv[i][0] == '-')
for (opt = argv[i] + 1; *opt; opt ++)
switch (*opt)
{
case 'a' :
op = ADD;
break;
case 'x' :
op = DELETE;
break;
case 'g' :
i ++;
if (i >= argc)
usage(stderr);
groupname = argv[i];
break;
case 'h' :
usage(stdout);
break;
default :
usage(stderr);
break;
}
else if (!username)
username = argv[i];
else
usage(stderr);
if (getuid() && (op != CHANGE || username))
{
fputs("lppasswd: Only root can add or delete passwords!\n", stderr);
return (1);
}
if (!username)
username = cupsUser();
oldpass = newpass = NULL;
if (op == CHANGE && getuid())
{
if ((passwd = cupsGetPassword("Enter old password:")) == NULL)
return (1);
if ((oldpass = strdup(passwd)) == NULL)
{
perror("lppasswd: Unable to copy password string");
return (1);
}
}
if (op != DELETE)
{
if ((passwd = cupsGetPassword("Enter password:")) == NULL)
return (1);
if ((newpass = strdup(passwd)) == NULL)
{
perror("lppasswd: Unable to copy password string!");
return (1);
}
if ((passwd = cupsGetPassword("Enter password again:")) == NULL)
return (1);
if (strcmp(passwd, newpass) != 0)
{
fputs("lppasswd: Sorry, passwords don't match!\n", stderr);
return (1);
}
flag = 0;
for (passwd = newpass; *passwd; passwd ++)
if (isdigit(*passwd & 255))
flag |= 1;
else if (isalpha(*passwd & 255))
flag |= 2;
if (strlen(newpass) < 6 || strstr(newpass, username) != NULL || flag != 3)
{
fputs("lppasswd: Sorry, password rejected.\n"
"Your password must be at least 6 characters long, cannot contain\n"
"your username, and must contain at least one letter and number.\n",
stderr);
return (1);
}
}
#ifndef WIN32
# if defined(HAVE_SIGSET)
sigset(SIGHUP, SIG_IGN);
sigset(SIGINT, SIG_IGN);
sigset(SIGTERM, SIG_IGN);
# ifdef SIGXFSZ
sigset(SIGXFSZ, SIG_IGN);
# endif
# elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigaction(SIGHUP, &action, NULL);
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
# ifdef SIGXFSZ
sigaction(SIGXFSZ, &action, NULL);
# endif
# else
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
# ifdef SIGXFSZ
signal(SIGXFSZ, SIG_IGN);
# endif
# endif
#endif
if ((fd = open(passwdnew, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0)
{
if (errno == EEXIST)
fputs("lppasswd: Password file busy!\n", stderr);
else
perror("lppasswd: Unable to open passwd file");
return (1);
}
if ((outfile = fdopen(fd, "w")) == NULL)
{
perror("lppasswd: Unable to open passwd file");
unlink(passwdnew);
return (1);
}
setbuf(outfile, NULL);
infile = fopen(passwdmd5, "r");
if (infile == NULL && errno != ENOENT && op != ADD)
{
perror("lppasswd: Unable to open password file");
fclose(outfile);
unlink(passwdnew);
return (1);
}
error = 0;
userline[0] = '\0';
groupline[0] = '\0';
md5line[0] = '\0';
if (infile)
{
while (fgets(line, sizeof(line), infile) != NULL)
{
if (sscanf(line, "%16[^:]:%16[^:]:%32s", userline, groupline, md5line) != 3)
continue;
if (strcmp(username, userline) == 0 &&
strcmp(groupname, groupline) == 0)
break;
if (fputs(line, outfile) == EOF)
{
perror("lppasswd: Unable to write to password file");
error = 1;
break;
}
}
if (!error)
{
while (fgets(line, sizeof(line), infile) != NULL)
if (fputs(line, outfile) == EOF)
{
perror("lppasswd: Unable to write to password file");
error = 1;
break;
}
}
}
if (op == CHANGE &&
(strcmp(username, userline) || strcmp(groupname, groupline)))
{
fprintf(stderr, "lppasswd: user \"%s\" and group \"%s\" do not exist.\n",
username, groupname);
error = 1;
}
else if (op != DELETE)
{
if (oldpass &&
strcmp(httpMD5(username, "CUPS", oldpass, md5new), md5line) != 0)
{
fputs("lppasswd: Sorry, password doesn't match!\n", stderr);
error = 1;
}
else
{
snprintf(line, sizeof(line), "%s:%s:%s\n", username, groupname,
httpMD5(username, "CUPS", newpass, md5new));
if (fputs(line, outfile) == EOF)
{
perror("lppasswd: Unable to write to password file");
error = 1;
}
}
}
if (infile)
fclose(infile);
if (fclose(outfile) == EOF)
error = 1;
if (error)
{
fputs("lppasswd: Password file not updated!\n", stderr);
unlink(passwdnew);
return (1);
}
unlink(passwdold);
if (link(passwdmd5, passwdold))
{
perror("lppasswd: failed to backup old password file");
unlink(passwdnew);
return (1);
}
if (rename(passwdnew, passwdmd5) < 0)
{
perror("lppasswd: failed to rename password file");
unlink(passwdnew);
return (1);
}
return (0);
}
static void
usage(FILE *fp)
{
if (getuid())
{
fputs("Usage: lppasswd [-g groupname]\n", fp);
}
else
{
fputs("Usage: lppasswd [-g groupname] [username]\n", fp);
fputs(" lppasswd [-g groupname] -a [username]\n", fp);
fputs(" lppasswd [-g groupname] -x [username]\n", fp);
}
exit(1);
}