First pass at cleaning up htpasswd. This removes the tmpnam call in

favor of apr_file_mktemp.  This also APR-izes most of the code.  The
file access code is still not using APR, but that will be fixed next.

I also re-organized some of the code to make it a little cleaner, and
to use functions where I could.

PR:	7764


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@95712 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan Bloom
2002-06-16 15:14:41 +00:00
parent 719c71e83f
commit 9149dd6fe1

View File

@ -134,54 +134,12 @@
#define ERR_OVERFLOW 5
#define ERR_BADUSER 6
/*
* This needs to be declared statically so the signal handler can
* access it.
*/
static char *tempfilename;
/*
* If our platform knows about the tmpnam() external buffer size, create
* a buffer to pass in. This is needed in a threaded environment, or
* one that thinks it is (like HP-UX).
*/
#ifdef L_tmpnam
static char tname_buf[L_tmpnam];
#else
static char *tname_buf = NULL;
#endif
#define NEWFILE 1
#define NOFILE 2
#define NONINTERACTIVE 4
/*
* Get a line of input from the user, not including any terminating
* newline.
*/
static int get_line(char *s, int n, FILE *f)
{
register int i = 0;
while (1) {
s[i] = (char) fgetc(f);
if (s[i] == CR) {
s[i] = fgetc(f);
}
if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}
static void putline(FILE *f, char *l)
{
int x;
for (x = 0; l[x]; x++) {
fputc(l[x], f);
}
fputc('\n', f);
}
apr_file_t *errfile;
apr_file_t *ftemp = NULL;
static void to64(char *s, unsigned long v, int n)
{
@ -194,6 +152,11 @@ static void to64(char *s, unsigned long v, int n)
}
}
static void putline(apr_file_t *f, const char *l)
{
apr_file_puts(l, f);
}
/*
* Make a password record from the given information. A zero return
* indicates success; failure means that the output buffer contains an
@ -276,43 +239,34 @@ static int mkrecord(char *user, char *record, size_t rlen, char *passwd,
return 0;
}
static int usage(void)
static void usage(void)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\thtpasswd [-cmdps] passwordfile username\n");
fprintf(stderr, "\thtpasswd -b[cmdps] passwordfile username password\n\n");
fprintf(stderr, "\thtpasswd -n[mdps] username\n");
fprintf(stderr, "\thtpasswd -nb[mdps] username password\n");
fprintf(stderr, " -c Create a new file.\n");
fprintf(stderr, " -n Don't update file; display results on stdout.\n");
fprintf(stderr, " -m Force MD5 encryption of the password"
apr_file_printf(errfile, "Usage:\n");
apr_file_printf(errfile, "\thtpasswd [-cmdps] passwordfile username\n");
apr_file_printf(errfile, "\thtpasswd -b[cmdps] passwordfile username password\n\n");
apr_file_printf(errfile, "\thtpasswd -n[mdps] username\n");
apr_file_printf(errfile, "\thtpasswd -nb[mdps] username password\n");
apr_file_printf(errfile, " -c Create a new file.\n");
apr_file_printf(errfile, " -n Don't update file; display results on stdout.\n");
apr_file_printf(errfile, " -m Force MD5 encryption of the password"
#if defined(WIN32) || defined(TPF) || defined(NETWARE)
" (default)"
#endif
".\n");
fprintf(stderr, " -d Force CRYPT encryption of the password"
apr_file_printf(errfile, " -d Force CRYPT encryption of the password"
#if (!(defined(WIN32) || defined(TPF) || defined(NETWARE)))
" (default)"
#endif
".\n");
fprintf(stderr, " -p Do not encrypt the password (plaintext).\n");
fprintf(stderr, " -s Force SHA encryption of the password.\n");
fprintf(stderr, " -b Use the password from the command line rather "
apr_file_printf(errfile, " -p Do not encrypt the password (plaintext).\n");
apr_file_printf(errfile, " -s Force SHA encryption of the password.\n");
apr_file_printf(errfile, " -b Use the password from the command line rather "
"than prompting for it.\n");
fprintf(stderr,
apr_file_printf(errfile,
"On Windows, NetWare and TPF systems the '-m' flag is used by default.\n");
fprintf(stderr,
apr_file_printf(errfile,
"On all other systems, the '-p' flag will probably not work.\n");
return ERR_SYNTAX;
}
static void interrupted(void)
{
fprintf(stderr, "Interrupted.\n");
if (tempfilename != NULL) {
unlink(tempfilename);
}
exit(ERR_INTERRUPTED);
exit(ERR_SYNTAX);
}
/*
@ -359,19 +313,6 @@ static int exists(char *fname, apr_pool_t *pool)
return ((check || sbuf.filetype != APR_REG) ? 0 : 1);
}
/*
* Copy from the current position of one file to the current position
* of another.
*/
static void copy_file(FILE *target, FILE *source)
{
static char line[MAX_STRING_LEN];
while (fgets(line, sizeof(line), source) != NULL) {
fputs(line, target);
}
}
#ifdef NETWARE
void nwTerminate()
{
@ -379,60 +320,12 @@ void nwTerminate()
}
#endif
/*
* Let's do it. We end up doing a lot of file opening and closing,
* but what do we care? This application isn't run constantly.
*/
int main(int argc, const char * const argv[])
static void check_args(int argc, const char *const argv[], int *alg, int *mask,
int *num_args)
{
FILE *ftemp = NULL;
FILE *fpw = NULL;
char user[MAX_STRING_LEN];
char password[MAX_STRING_LEN];
char record[MAX_STRING_LEN];
char line[MAX_STRING_LEN];
char pwfilename[MAX_STRING_LEN];
const char *arg;
int found = 0;
int alg = ALG_CRYPT;
int newfile = 0;
int nofile = 0;
int noninteractive = 0;
int i;
int args_left = 2;
apr_pool_t *pool;
#if APR_CHARSET_EBCDIC
apr_status_t rv;
apr_xlate_t *to_ascii;
#endif
apr_app_initialize(&argc, &argv, NULL);
atexit(apr_terminate);
#ifdef NETWARE
atexit(nwTerminate);
#endif
apr_pool_create(&pool, NULL);
#if APR_CHARSET_EBCDIC
rv = apr_xlate_open(&to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, pool);
if (rv) {
fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
exit(1);
}
rv = apr_SHA1InitEBCDIC(to_ascii);
if (rv) {
fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
exit(1);
}
rv = apr_MD5InitEBCDIC(to_ascii);
if (rv) {
fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
exit(1);
}
#endif /*APR_CHARSET_EBCDIC*/
tempfilename = NULL;
apr_signal(SIGINT, (void (*)(int)) interrupted);
int i;
/*
* Preliminary check to make sure they provided at least
@ -440,7 +333,7 @@ int main(int argc, const char * const argv[])
* we parse the command line.
*/
if (argc < 3) {
return usage();
usage();
}
/*
@ -454,98 +347,154 @@ int main(int argc, const char * const argv[])
}
while (*++arg != '\0') {
if (*arg == 'c') {
newfile++;
*mask |= NEWFILE;
}
else if (*arg == 'n') {
nofile++;
*mask |= NOFILE;
args_left--;
}
else if (*arg == 'm') {
alg = ALG_APMD5;
*alg = ALG_APMD5;
}
else if (*arg == 's') {
alg = ALG_APSHA;
*alg = ALG_APSHA;
}
else if (*arg == 'p') {
alg = ALG_PLAIN;
*alg = ALG_PLAIN;
}
else if (*arg == 'd') {
alg = ALG_CRYPT;
*alg = ALG_CRYPT;
}
else if (*arg == 'b') {
noninteractive++;
*mask |= NONINTERACTIVE;
args_left++;
}
else {
return usage();
usage();
}
}
}
if (*mask & (NEWFILE & NOFILE)) {
apr_file_printf(errfile, "%s: -c and -n options conflict\n", argv[0]);
exit(ERR_SYNTAX);
}
/*
* Make sure we still have exactly the right number of arguments left
* (the filename, the username, and possibly the password if -b was
* specified).
*/
if ((argc - i) != args_left) {
return usage();
usage();
}
if (newfile && nofile) {
fprintf(stderr, "%s: -c and -n options conflict\n", argv[0]);
return ERR_SYNTAX;
*num_args = i;
}
/*
* Let's do it. We end up doing a lot of file opening and closing,
* but what do we care? This application isn't run constantly.
*/
int main(int argc, const char * const argv[])
{
apr_file_t *fpw = NULL;
char user[MAX_STRING_LEN];
char record[MAX_STRING_LEN];
char line[MAX_STRING_LEN];
char *password = NULL;
char *pwfilename = NULL;
char tn[] = "htpasswd.tmp.XXXXXX";
char scratch[MAX_STRING_LEN];
char *str = NULL;
int found = 0;
int alg = ALG_CRYPT;
int mask = 0;
int i;
apr_pool_t *pool;
#if APR_CHARSET_EBCDIC
apr_status_t rv;
apr_xlate_t *to_ascii;
#endif
apr_app_initialize(&argc, &argv, NULL);
atexit(apr_terminate);
#ifdef NETWARE
atexit(nwTerminate);
#endif
apr_pool_create(&pool, NULL);
apr_file_open_stderr(&errfile, pool);
#if APR_CHARSET_EBCDIC
rv = apr_xlate_open(&to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, pool);
if (rv) {
apr_file_printf(errfile, "apr_xlate_open(to ASCII)->%d\n", rv);
exit(1);
}
if (nofile) {
rv = apr_SHA1InitEBCDIC(to_ascii);
if (rv) {
apr_file_printf(errfile, "apr_SHA1InitEBCDIC()->%d\n", rv);
exit(1);
}
rv = apr_MD5InitEBCDIC(to_ascii);
if (rv) {
apr_file_printf(errfile, "apr_MD5InitEBCDIC()->%d\n", rv);
exit(1);
}
#endif /*APR_CHARSET_EBCDIC*/
check_args(argc, argv, &alg, &mask, &i);
if (mask & NOFILE) {
i--;
}
else {
if (strlen(argv[i]) > (sizeof(pwfilename) - 1)) {
fprintf(stderr, "%s: filename too long\n", argv[0]);
if (strlen(argv[i]) > (PATH_MAX - 1)) {
apr_file_printf(errfile, "%s: filename too long\n", argv[0]);
return ERR_OVERFLOW;
}
strcpy(pwfilename, argv[i]);
pwfilename = apr_pstrdup(pool, argv[i]);
if (strlen(argv[i + 1]) > (sizeof(user) - 1)) {
fprintf(stderr, "%s: username too long (>%" APR_SIZE_T_FMT ")\n",
apr_file_printf(errfile, "%s: username too long (>%" APR_SIZE_T_FMT ")\n",
argv[0], sizeof(user) - 1);
return ERR_OVERFLOW;
}
}
strcpy(user, argv[i + 1]);
if ((arg = strchr(user, ':')) != NULL) {
fprintf(stderr, "%s: username contains illegal character '%c'\n",
argv[0], *arg);
if ((str = strchr(user, ':')) != NULL) {
apr_file_printf(errfile, "%s: username contains illegal character '%c'\n",
argv[0], *str);
return ERR_BADUSER;
}
if (noninteractive) {
if (strlen(argv[i + 2]) > (sizeof(password) - 1)) {
fprintf(stderr, "%s: password too long (>%" APR_SIZE_T_FMT ")\n",
argv[0], sizeof(password) - 1);
if (mask & NONINTERACTIVE) {
if (strlen(argv[i + 2]) > (MAX_STRING_LEN - 1)) {
apr_file_printf(errfile, "%s: password too long (>%" APR_SIZE_T_FMT ")\n",
argv[0], MAX_STRING_LEN);
return ERR_OVERFLOW;
}
strcpy(password, argv[i + 2]);
password = apr_pstrdup(pool, argv[i + 2]);
}
#if defined(WIN32) || defined(NETWARE)
if (alg == ALG_CRYPT) {
alg = ALG_APMD5;
fprintf(stderr, "Automatically using MD5 format.\n");
apr_file_printf(errfile, "Automatically using MD5 format.\n");
}
#endif
#if (!(defined(WIN32) || defined(TPF) || defined(NETWARE)))
if (alg == ALG_PLAIN) {
fprintf(stderr,"Warning: storing passwords as plain text might "
apr_file_printf(errfile,"Warning: storing passwords as plain text might "
"just not work on this platform.\n");
}
#endif
if (! nofile) {
if (! mask & NOFILE) {
/*
* Only do the file checks if we're supposed to frob it.
*
* Verify that the file exists if -c was omitted. We give a special
* message if it doesn't.
*/
if ((! newfile) && (! exists(pwfilename, pool))) {
fprintf(stderr,
if ((! mask & NEWFILE) && (! exists(pwfilename, pool))) {
apr_file_printf(errfile,
"%s: cannot modify file %s; use '-c' to create it\n",
argv[0], pwfilename);
perror("fopen");
@ -555,8 +504,8 @@ int main(int argc, const char * const argv[])
* Verify that we can read the existing file in the case of an update
* to it (rather than creation of a new one).
*/
if ((! newfile) && (! readable(pwfilename))) {
fprintf(stderr, "%s: cannot open file %s for read access\n",
if ((! mask & NEWFILE) && (! readable(pwfilename))) {
apr_file_printf(errfile, "%s: cannot open file %s for read access\n",
argv[0], pwfilename);
perror("fopen");
exit(ERR_FILEPERM);
@ -565,8 +514,8 @@ int main(int argc, const char * const argv[])
* Now check to see if we can preserve an existing file in case
* of password verification errors on a -c operation.
*/
if (newfile && exists(pwfilename, pool) && (! readable(pwfilename))) {
fprintf(stderr, "%s: cannot open file %s for read access\n"
if ((mask & NEWFILE) && exists(pwfilename, pool) && (! readable(pwfilename))) {
apr_file_printf(errfile, "%s: cannot open file %s for read access\n"
"%s: existing auth data would be lost on "
"password mismatch",
argv[0], pwfilename, argv[0]);
@ -577,7 +526,7 @@ int main(int argc, const char * const argv[])
* Now verify that the file is writable!
*/
if (! writable(pwfilename)) {
fprintf(stderr, "%s: cannot open file %s for write access\n",
apr_file_printf(errfile, "%s: cannot open file %s for write access\n",
argv[0], pwfilename);
perror("fopen");
exit(ERR_FILEPERM);
@ -592,13 +541,12 @@ int main(int argc, const char * const argv[])
* the mkrecord() routine doesn't have access to argv[].
*/
i = mkrecord(user, record, sizeof(record) - 1,
noninteractive ? password : NULL,
alg);
password, alg);
if (i != 0) {
fprintf(stderr, "%s: %s\n", argv[0], record);
apr_file_printf(errfile, "%s: %s\n", argv[0], record);
exit(i);
}
if (nofile) {
if (mask & NOFILE) {
printf("%s\n", record);
exit(0);
}
@ -607,33 +555,21 @@ int main(int argc, const char * const argv[])
* We can access the files the right way, and we have a record
* to add or update. Let's do it..
*/
errno = 0;
tempfilename = tmpnam(tname_buf);
if ((tempfilename == NULL) || (*tempfilename == '\0')) {
fprintf(stderr, "%s: unable to generate temporary filename\n",
argv[0]);
if (errno == 0) {
errno = ENOENT;
}
perror("tmpnam");
exit(ERR_FILEPERM);
}
ftemp = fopen(tempfilename, "w+");
if (ftemp == NULL) {
fprintf(stderr, "%s: unable to create temporary file '%s'\n", argv[0],
tempfilename);
perror("fopen");
if (apr_file_mktemp(&ftemp, tn,
APR_CREATE | APR_READ | APR_WRITE | APR_EXCL,
pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: unable to create temporary file '%s'\n",
argv[0], tn);
exit(ERR_FILEPERM);
}
/*
* If we're not creating a new file, copy records from the existing
* one to the temporary file until we find the specified user.
*/
if (! newfile) {
char scratch[MAX_STRING_LEN];
fpw = fopen(pwfilename, "r");
while (! (get_line(line, sizeof(line), fpw))) {
if (apr_file_open(&fpw, pwfilename, APR_READ, APR_OS_DEFAULT,
pool) == APR_SUCCESS) {
while (! (apr_file_gets(line, sizeof(line), fpw))) {
char *colon;
if ((line[0] == '#') || (line[0] == '\0')) {
@ -652,40 +588,29 @@ int main(int argc, const char * const argv[])
putline(ftemp, line);
continue;
}
found++;
break;
else {
/* We found the user we were looking for, add him to the file.
*/
apr_file_printf(errfile, "Updating ");
putline(ftemp, record);
}
}
}
if (found) {
fprintf(stderr, "Updating ");
if (!found) {
apr_file_printf(errfile, "Adding ");
putline(ftemp, record);
}
else {
fprintf(stderr, "Adding ");
}
fprintf(stderr, "password for user %s\n", user);
/*
* Now add the user record we created.
apr_file_printf(errfile, "password for user %s\n", user);
apr_file_close(fpw);
/* The temporary file has all the data, just copy it to the new location.
*/
putline(ftemp, record);
/*
* If we're updating an existing file, there may be additional
* records beyond the one we're updating, so copy them.
*/
if (! newfile) {
copy_file(ftemp, fpw);
fclose(fpw);
}
/*
* The temporary file now contains the information that should be
* in the actual password file. Close the open files, re-open them
* in the appropriate mode, and copy them file to the real one.
*/
fclose(ftemp);
fpw = fopen(pwfilename, "w+");
ftemp = fopen(tempfilename, "r");
copy_file(fpw, ftemp);
fclose(fpw);
fclose(ftemp);
unlink(tempfilename);
#if defined(OS2) || defined(WIN32)
str = apr_psprintf(pool, "copy \"%s\" \"%s\"", tn, pwfilename);
#else
str = apr_psprintf(pool, "cp %s %s", tn, pwfilename);
#endif
system(str);
apr_file_close(ftemp);
return 0;
}