/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/****************************************************************************** ****************************************************************************** * NOTE! This program is not safe as a setuid executable! Do not make it * setuid! ******************************************************************************
*****************************************************************************/ /* * htpasswd.c: simple program for manipulating password file for * the Apache HTTP server * * Originally by Rob McCool * * Exit values: * 0: Success * 1: Failure; file access/permission problem * 2: Failure; command line syntax problem (usage message issued) * 3: Failure; password verification failure * 4: Failure; operation interrupted (such as with CTRL/C) * 5: Failure; buffer would overflow (username, filename, or computed * record too long) * 6: Failure; username contains illegal or reserved characters * 7: Failure; file is not a valid htpasswd file
*/
ret = mkhash(ctx); if (ret) {
ctx->out = NULL;
ctx->out_len = 0; return ret;
}
ctx->out = apr_pstrcat(ctx->pool, user, ":", hash_str, NL, NULL);
ctx->out_len = strlen(ctx->out); if (ctx->out_len >= MAX_STRING_LEN) {
ctx->errstr = "resultant record too long"; return ERR_OVERFLOW;
} return 0;
}
staticvoid usage(void)
{
apr_file_printf(errfile, "Usage:" NL "\thtpasswd [-cimB25dpsDv] [-C cost] [-r rounds] passwordfile username" NL "\thtpasswd -b[cmB25dpsDv] [-C cost] [-r rounds] passwordfile username password"NL
NL "\thtpasswd -n[imB25dps] [-C cost] [-r rounds] username" NL "\thtpasswd -nb[mB25dps] [-C cost] [-r rounds] username password" NL " -c Create a new file." NL " -n Don't update file; display results on stdout." NL " -b Use the password from the command line rather than prompting " "for it." NL " -i Read password from stdin without verification (for script usage)." NL " -m Force MD5 hashing of the password (default)." NL " -2 Force SHA-256 hashing of the password (secure)." NL " -5 Force SHA-512 hashing of the password (secure)." NL " -B Force bcrypt hashing of the password (very secure)." NL " -C Set the computing time used for the bcrypt algorithm" NL " (higher is more secure but slower, default: %d, valid: 4 to 17)." NL " -r Set the number of rounds used for the SHA-256, SHA-512 algorithms" NL " (higher is more secure but slower, default: 5000)." NL " -d Force CRYPT hashing of the password (8 chars max, insecure)." NL " -s Force SHA-1 hashing of the password (insecure)." NL " -p Do not hash the password (plaintext, insecure)." NL " -D Delete the specified user." NL " -v Verify password for the specified user." NL "On other systems than Windows and NetWare the '-p' flag will " "probably not work." NL "The SHA-1 algorithm does not use a salt and is less secure than the " "MD5 algorithm." NL,
BCRYPT_DEFAULT_COST
); exit(ERR_SYNTAX);
}
/* * Check to see if the specified file can be opened for the given * access.
*/ staticint accessible(apr_pool_t *pool, char *fname, int mode)
{
apr_file_t *f = NULL;
if ((*mask) & (*mask - 1)) { /* not a power of two, i.e. more than one flag specified */
apr_file_printf(errfile, "%s: only one of -c -n -v -D may be specified" NL,
argv[0]); exit(ERR_SYNTAX);
} if ((*mask & APHTP_VERIFY) && ctx->passwd_src == PW_PROMPT)
ctx->passwd_src = PW_PROMPT_VERIFY;
/* * Make sure we still have exactly the right number of arguments left * (the filename, the username, and possibly the password if -b was * specified).
*/
i = state->ind; if ((argc - i) != args_left) {
usage();
}
if (!(*mask & APHTP_NOFILE)) { if (strlen(argv[i]) > (APR_PATH_MAX - 1)) {
apr_file_printf(errfile, "%s: filename too long" NL, argv[0]); exit(ERR_OVERFLOW);
}
*pwfilename = apr_pstrdup(pool, argv[i++]);
} if (strlen(argv[i]) > (MAX_STRING_LEN - 1)) {
apr_file_printf(errfile, "%s: username too long (> %d)" NL,
argv[0], MAX_STRING_LEN - 1); exit(ERR_OVERFLOW);
}
*user = apr_pstrdup(pool, argv[i++]); if ((arg = strchr(*user, ':')) != NULL) {
apr_file_printf(errfile, "%s: username contains illegal " "character '%c'" NL, argv[0], *arg); exit(ERR_BADUSER);
} if (ctx->passwd_src == PW_ARG) { if (strlen(argv[i]) > (MAX_STRING_LEN - 1)) {
apr_file_printf(errfile, "%s: password too long (> %d)" NL,
argv[0], MAX_STRING_LEN); exit(ERR_OVERFLOW);
}
ctx->passwd = apr_pstrdup(pool, argv[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, constchar * const argv[])
{
apr_file_t *fpw = NULL; char line[MAX_STRING_LEN]; char *pwfilename = NULL; char *user = NULL; char tn[] = "htpasswd.tmp.XXXXXX"; char *dirname; char *scratch, cp[MAX_STRING_LEN]; int found = 0; int i; unsigned mask = 0;
apr_pool_t *pool; int existing_file = 0; struct passwd_ctx ctx = { 0 }; #if APR_CHARSET_EBCDIC
apr_status_t rv;
apr_xlate_t *to_ascii; #endif
/* * Only do the file checks if we're supposed to frob it.
*/ if (!(mask & APHTP_NOFILE)) {
existing_file = exists(pwfilename, pool); if (existing_file && (mask & APHTP_VERIFY) == 0) { /* * Check that this existing file is readable and writable.
*/ if (!accessible(pool, pwfilename, APR_FOPEN_READ|APR_FOPEN_WRITE)) {
apr_file_printf(errfile, "%s: cannot open file %s for " "read/write access" NL, argv[0], pwfilename); exit(ERR_FILEPERM);
}
} elseif (existing_file && (mask & APHTP_VERIFY) != 0) { /* * Check that this existing file is readable.
*/ if (!accessible(pool, pwfilename, APR_FOPEN_READ)) {
apr_file_printf(errfile, "%s: cannot open file %s for " "read access" NL, argv[0], pwfilename); exit(ERR_FILEPERM);
}
} else { /* * Error out if -c was omitted for this non-existent file.
*/ if (!(mask & APHTP_NEWFILE)) {
apr_file_printf(errfile, "%s: cannot modify file %s; use '-c' to create it" NL,
argv[0], pwfilename); exit(ERR_FILEPERM);
} /* * As it doesn't exist yet, verify that we can create it.
*/ if (!accessible(pool, pwfilename, APR_FOPEN_WRITE|APR_FOPEN_CREATE)) {
apr_file_printf(errfile, "%s: cannot create file %s" NL,
argv[0], pwfilename); exit(ERR_FILEPERM);
}
}
}
/* * All the file access checks (if any) have been made. Time to go to work; * try to create the record for the username in question. If that * fails, there's no need to waste any time on file manipulations. * Any error message text is returned in the record buffer, since * the mkrecord() routine doesn't have access to argv[].
*/ if ((mask & (APHTP_DELUSER|APHTP_VERIFY)) == 0) {
i = mkrecord(&ctx, user); if (i != 0) {
apr_file_printf(errfile, "%s: %s" NL, argv[0], ctx.errstr); exit(i);
} if (mask & APHTP_NOFILE) {
printf("%s" NL, ctx.out); exit(0);
}
}
if ((mask & APHTP_VERIFY) == 0) { /* * We can access the files the right way, and we have a record * to add or update. Let's do it..
*/ if (apr_temp_dir_get((constchar**)&dirname, pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: could not determine temp dir" NL,
argv[0]); exit(ERR_FILEPERM);
}
dirname = apr_psprintf(pool, "%s/%s", dirname, tn);
/* * 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 (existing_file && !(mask & APHTP_NEWFILE)) { if (apr_file_open(&fpw, pwfilename, APR_READ | APR_BUFFERED,
APR_OS_DEFAULT, pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: unable to read file %s" NL,
argv[0], pwfilename); exit(ERR_FILEPERM);
} while (apr_file_gets(line, sizeof(line), fpw) == APR_SUCCESS) { char *colon;
strcpy(cp, line);
scratch = cp; while (apr_isspace(*scratch)) {
++scratch;
}
if (!*scratch || (*scratch == '#')) {
putline(ftemp, line); continue;
} /* * See if this is our user.
*/
colon = strchr(scratch, ':'); if (colon != NULL) {
*colon = '\0';
} else { /* * If we've not got a colon on the line, this could well * not be a valid htpasswd file. * We should bail at this point.
*/
apr_file_printf(errfile, "%s: The file %s does not appear " "to be a valid htpasswd file." NL,
argv[0], pwfilename);
apr_file_close(fpw); exit(ERR_INVALID);
} if (strcmp(user, scratch) != 0) {
putline(ftemp, line); continue;
} else { /* We found the user we were looking for */
found++; if ((mask & APHTP_DELUSER)) { /* Delete entry from the file */
apr_file_printf(errfile, "Deleting ");
} elseif ((mask & APHTP_VERIFY)) { /* Verify */ char *hash = colon + 1;
size_t len;
len = strcspn(hash, "\r\n"); if (len == 0) {
apr_file_printf(errfile, "Empty hash for user %s" NL,
user); exit(ERR_INVALID);
}
hash[len] = '\0';
i = verify(&ctx, hash); if (i != 0) {
apr_file_printf(errfile, "%s" NL, ctx.errstr); exit(i);
}
} else { /* Update entry */
apr_file_printf(errfile, "Updating ");
putline(ftemp, ctx.out);
}
}
}
apr_file_close(fpw);
} if (!found) { if (mask & APHTP_DELUSER) {
apr_file_printf(errfile, "User %s not found" NL, user); exit(0);
} elseif (mask & APHTP_VERIFY) {
apr_file_printf(errfile, "User %s not found" NL, user); exit(ERR_BADUSER);
} else {
apr_file_printf(errfile, "Adding ");
putline(ftemp, ctx.out);
}
} if (mask & APHTP_VERIFY) {
apr_file_printf(errfile, "Password for user %s correct." NL, user); exit(0);
}
apr_file_printf(errfile, "password for user %s" NL, user);
/* The temporary file has all the data, just copy it to the new location.
*/ if (apr_file_copy(dirname, pwfilename, APR_OS_DEFAULT, pool) !=
APR_SUCCESS) {
apr_file_printf(errfile, "%s: unable to update file %s" NL,
argv[0], pwfilename); exit(ERR_FILEPERM);
}
apr_file_close(ftemp); return 0;
}
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.