Package: apache2-utils Version: 2.0.54-4 Severity: wishlist File: /usr/bin/htdigest Tags: patch
In some cases it may be useful to manage digest files noninteractively. The attached patch prototypes a way of doing that (well, it works, but it doesn't use APR as extensively as it probably should). It adds an option "-f" that causes the new password to be read from a file--or from stdin if "-" is used as a filename--without prompting for confirmation. Some minor cleanups were necessary; all argv "arithmetic" is in one place now, as is reading of the actual password. Some buffer copying of command line arguments looks like it may be unneeded, but I left it in place to reduce the risk of screwups. -- System Information: Debian Release: 3.1 APT prefers unstable APT policy: (50, 'unstable') Architecture: i386 (i686) Shell: /bin/sh linked to /bin/bash Kernel: Linux 2.6.11 Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) Versions of packages apache2-utils depends on: ii libapr0 2.0.54-4 the Apache Portable Runtime ii libc6 2.3.2.ds1-22 GNU C Library: Shared libraries an ii libdb4.2 4.2.52-18 Berkeley v4.2 Database Libraries [ ii libexpat1 1.95.8-3 XML parsing C library - runtime li ii libldap2 2.1.30-10 OpenLDAP libraries ii libpcre3 5.0-1.1 Perl 5 Compatible Regular Expressi ii libssl0.9.7 0.9.7g-1 SSL shared libraries ii zlib1g 1:1.2.2-4.sarge.2 compression library - runtime -- no debconf information
diff -ru httpd-2.0.54.org/docs/manual/programs/htdigest.xml httpd-2.0.54.patch/docs/manual/programs/htdigest.xml --- httpd-2.0.54.org/docs/manual/programs/htdigest.xml 2005-02-05 03:21:18.000000000 +0700 +++ httpd-2.0.54.patch/docs/manual/programs/htdigest.xml 2005-08-29 14:37:12.807750434 +0700 @@ -43,7 +43,9 @@ <section id="synopsis"><title>Synopsis</title> <p><code><strong>htdigest</strong> [ -<strong>c</strong> ] <var>passwdfile</var> <var>realm</var> <var>username</var></code></p> -</section> + <p><code><strong>htdigest</strong> -<strong>f</strong> [ -<strong>c</strong> ] + <var>passwdfile</var> <var>realm</var> <var>username</var> <var>inputfile</var> +</code></p></section> <section id="options"><title>Options</title> <dl> @@ -64,6 +66,14 @@ <var>username</var> does not exist is this file, an entry is added. If it does exist, the password is changed.</dd> </dl> + <dl> + <dt><code>-f</code></dt> + <dd>Don't prompt for password; open <var>inputfile</var> and read the new + password from there instead. Only the first line is read, which may not be + empty. As a special case, if <var>inputfile</var> is just a single hyphen + (<strong>-</strong>), standard input will be read as if it were a regular + file. The user is not prompted to repeat the password for confirmation.</dd> + </dl> </section> </manualpage> diff -ru httpd-2.0.54.org/support/htdigest.c httpd-2.0.54.patch/support/htdigest.c --- httpd-2.0.54.org/support/htdigest.c 2005-03-01 17:02:06.000000000 +0700 +++ httpd-2.0.54.patch/support/htdigest.c 2005-08-29 17:41:51.095844118 +0700 @@ -67,6 +67,8 @@ apr_xlate_t *to_ascii; #endif + + static void cleanup_tempfile_and_exit(int rc) { if (tfp) { @@ -119,28 +121,94 @@ } -static void add_password(const char *user, const char *realm, apr_file_t *f) +/* Ask for password entered interactively. + * + * Returns a program exit code on failure, or 0 for success. + */ +static int getpw_interactive(char buf[], apr_size_t len) +{ + char confirmbuf[MAX_STRING_LEN]; + + apr_size_t tmplen = len; + if (apr_password_get("New password: ", buf, &tmplen) != APR_SUCCESS) { + apr_file_printf(errfile, "password too long\n"); + return 5; + } + tmplen = sizeof(confirmbuf); + apr_password_get("Re-type new password: ", confirmbuf, &tmplen); + if (strcmp(buf, confirmbuf) != 0) { + apr_file_printf(errfile, "They don't match, sorry.\n"); + return 1; + } + + return 0; +} + + +/* Read new password from file called pwfile. + * + * Returns a program exit code on failure, or 0 for success. + */ +static int getpw_fromfile(char buf[], apr_size_t len, const char pw_inputfile[]) +{ + FILE *infile = NULL; + const char *c; + int close_infile = 0; + int e; + size_t s; + + /* TODO: So far, these return codes are entirely arbitrary. */ + + /* Open password input file ("-" means standard input) */ + if (strcmp(pw_inputfile, "-") == 0) { + infile = stdin; + if (!infile) { + apr_file_printf(errfile, "standard input not available\n"); + return 1; + } + } + else { + infile = fopen(pw_inputfile, "rb"); + if (!infile) { + const int e = errno; + apr_file_printf(errfile, + "could not open '%s' for reading: %s\n", + pw_inputfile, + strerror(e)); + return 1; + } + close_infile = 1; + } + + c = fgets(buf, len, infile); + e = errno; + if (close_infile) fclose(infile); + + if (!c || !buf[0]) { + apr_file_printf(errfile, + "could not read password: %s\n", + strerror(e)); + return 1; + } + + s = strlen(buf); + if (s && buf[s-1] == '\n') buf[s-1] = '\0'; + + return 0; +} + + +static void add_password(const char *user, + const char *realm, + apr_file_t *f, + const char pw[]) { - char *pw; apr_md5_ctx_t context; unsigned char digest[16]; char string[MAX_STRING_LEN]; - char pwin[MAX_STRING_LEN]; - char pwv[MAX_STRING_LEN]; - unsigned int i; - apr_size_t len = sizeof(pwin); - - if (apr_password_get("New password: ", pwin, &len) != APR_SUCCESS) { - apr_file_printf(errfile, "password too long"); - cleanup_tempfile_and_exit(5); - } - len = sizeof(pwin); - apr_password_get("Re-type new password: ", pwv, &len); - if (strcmp(pwin, pwv) != 0) { - apr_file_printf(errfile, "They don't match, sorry.\n"); - cleanup_tempfile_and_exit(1); - } - pw = pwin; + int i; + int errcode; + apr_file_printf(f, "%s:%s:", user, realm); /* Do MD5 stuff */ @@ -162,7 +230,10 @@ static void usage(void) { apr_file_printf(errfile, "Usage: htdigest [-c] passwordfile realm username\n"); - apr_file_printf(errfile, "The -c flag creates a new file.\n"); + apr_file_printf(errfile, "or htdigest -f [-c] passwordfile realm username inputfile\n"); + apr_file_printf(errfile, "The -c flag creates a new file. The -f flag will cause the new password to be\n" + "read from a given input file (or stdin if the given filename is '-'), in which\n" + "case there will be no prompt for confirmation.\n"); exit(1); } @@ -192,7 +263,15 @@ char l[MAX_STRING_LEN]; char w[MAX_STRING_LEN]; char x[MAX_STRING_LEN]; + char pw[MAX_STRING_LEN]; int found; + int createfile = 0, readfromfile = 0; + int pwres; + int i; + const char *arg_passwdfile = NULL; + const char *arg_realm = NULL; + const char *arg_username = NULL; + const char *arg_inputfile = NULL; apr_app_initialize(&argc, &argv, NULL); atexit(terminate); @@ -209,27 +288,50 @@ #endif apr_signal(SIGINT, (void (*)(int)) interrupted); - if (argc == 5) { - if (strcmp(argv[1], "-c")) - usage(); - rv = apr_file_open(&f, argv[2], APR_WRITE | APR_CREATE, + + for (i = 1; i < 3 && argv[i]; ++i) { + if (strcmp(argv[i],"-c")==0) ++createfile; + else if (strcmp(argv[i],"-f")==0) ++readfromfile; + } + + /* The -c option adds 1 argument to the list; -f adds two (itself plus an + * input filename). Given that, check for the right number of remaining + * arguments. + */ + if (createfile > 1 || + readfromfile > 1 || + (argc-1-createfile-2*readfromfile) != 3) { + usage(); + exit(2); + } + + memset(pw, 0, sizeof(pw)); + arg_passwdfile = argv[1+createfile+readfromfile]; + arg_realm = argv[2+createfile+readfromfile]; + arg_username = argv[3+createfile+readfromfile]; + if (readfromfile) arg_inputfile = argv[4+createfile+readfromfile]; + + + if (readfromfile) pwres = getpw_fromfile(pw, sizeof(pw), arg_inputfile); + else pwres = getpw_interactive(pw, sizeof(pw)); + if (pwres != 0) return pwres; + if (createfile) { + rv = apr_file_open(&f, arg_passwdfile, APR_WRITE | APR_CREATE, APR_OS_DEFAULT, cntxt); if (rv != APR_SUCCESS) { char errmsg[120]; apr_file_printf(errfile, "Could not open passwd file %s for writing: %s\n", - argv[2], + arg_passwdfile, apr_strerror(rv, errmsg, sizeof errmsg)); exit(1); } apr_file_printf(errfile, "Adding password for %s in realm %s.\n", - argv[4], argv[3]); - add_password(argv[4], argv[3], f); + arg_username, arg_realm); + add_password(arg_username, arg_realm, f, pw); apr_file_close(f); exit(0); } - else if (argc != 4) - usage(); if (apr_temp_dir_get((const char**)&dirname, cntxt) != APR_SUCCESS) { apr_file_printf(errfile, "%s: could not determine temp dir\n", @@ -243,14 +345,14 @@ exit(1); } - if (apr_file_open(&f, argv[1], APR_READ, APR_OS_DEFAULT, cntxt) != APR_SUCCESS) { + if (apr_file_open(&f, arg_passwdfile, APR_READ, APR_OS_DEFAULT, cntxt) != APR_SUCCESS) { apr_file_printf(errfile, "Could not open passwd file %s for reading.\n", argv[1]); apr_file_printf(errfile, "Use -c option to create new one.\n"); cleanup_tempfile_and_exit(1); } - apr_cpystrn(user, argv[3], sizeof(user)); - apr_cpystrn(realm, argv[2], sizeof(realm)); + apr_cpystrn(user, arg_username, sizeof(user)); + apr_cpystrn(realm, arg_realm, sizeof(realm)); found = 0; while (!(get_line(line, MAX_STRING_LEN, f))) { @@ -268,22 +370,22 @@ else { apr_file_printf(errfile, "Changing password for user %s in realm %s\n", user, realm); - add_password(user, realm, tfp); + add_password(user, realm, tfp, pw); found = 1; } } if (!found) { apr_file_printf(errfile, "Adding user %s in realm %s\n", user, realm); - add_password(user, realm, tfp); + add_password(user, realm, tfp, pw); } apr_file_close(f); /* The temporary file has all the data, just copy it to the new location. */ - if (apr_file_copy(dirname, argv[1], APR_FILE_SOURCE_PERMS, cntxt) != + if (apr_file_copy(dirname, arg_passwdfile, APR_FILE_SOURCE_PERMS, cntxt) != APR_SUCCESS) { apr_file_printf(errfile, "%s: unable to update file %s\n", - argv[0], argv[1]); + argv[0], arg_passwdfile); } apr_file_close(tfp);