00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "apr.h"
00042 #include "apr_lib.h"
00043 #include "apr_strings.h"
00044 #include "apr_errno.h"
00045 #include "apr_file_io.h"
00046 #include "apr_general.h"
00047 #include "apr_signal.h"
00048
00049 #if APR_HAVE_STDIO_H
00050 #include <stdio.h>
00051 #endif
00052
00053 #include "apr_md5.h"
00054 #include "apr_sha1.h"
00055 #include <time.h>
00056
00057 #if APR_HAVE_CRYPT_H
00058 #include <crypt.h>
00059 #endif
00060 #if APR_HAVE_STDLIB_H
00061 #include <stdlib.h>
00062 #endif
00063 #if APR_HAVE_STRING_H
00064 #include <string.h>
00065 #endif
00066 #if APR_HAVE_UNISTD_H
00067 #include <unistd.h>
00068 #endif
00069
00070 #ifdef WIN32
00071 #include <conio.h>
00072 #define unlink _unlink
00073 #endif
00074
00075 #if !APR_CHARSET_EBCDIC
00076 #define LF 10
00077 #define CR 13
00078 #else
00079 #define LF '\n'
00080 #define CR '\r'
00081 #endif
00082
00083 #define MAX_STRING_LEN 256
00084 #define ALG_PLAIN 0
00085 #define ALG_CRYPT 1
00086 #define ALG_APMD5 2
00087 #define ALG_APSHA 3
00088
00089 #define ERR_FILEPERM 1
00090 #define ERR_SYNTAX 2
00091 #define ERR_PWMISMATCH 3
00092 #define ERR_INTERRUPTED 4
00093 #define ERR_OVERFLOW 5
00094 #define ERR_BADUSER 6
00095 #define ERR_INVALID 7
00096
00097 #define APHTP_NEWFILE 1
00098 #define APHTP_NOFILE 2
00099 #define APHTP_NONINTERACTIVE 4
00100 #define APHTP_DELUSER 8
00101
00102 apr_file_t *errfile;
00103 apr_file_t *ftemp = NULL;
00104
00105 static void to64(char *s, unsigned long v, int n)
00106 {
00107 static unsigned char itoa64[] =
00108 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
00109
00110 while (--n >= 0) {
00111 *s++ = itoa64[v&0x3f];
00112 v >>= 6;
00113 }
00114 }
00115
00116 static void putline(apr_file_t *f, const char *l)
00117 {
00118 apr_file_puts(l, f);
00119 }
00120
00121
00122
00123
00124
00125
00126 static int mkrecord(char *user, char *record, apr_size_t rlen, char *passwd,
00127 int alg)
00128 {
00129 char *pw;
00130 char cpw[120];
00131 char pwin[MAX_STRING_LEN];
00132 char pwv[MAX_STRING_LEN];
00133 char salt[9];
00134 apr_size_t bufsize;
00135
00136 if (passwd != NULL) {
00137 pw = passwd;
00138 }
00139 else {
00140 bufsize = sizeof(pwin);
00141 if (apr_password_get("New password: ", pwin, &bufsize) != 0) {
00142 apr_snprintf(record, (rlen - 1), "password too long (>%"
00143 APR_SIZE_T_FMT ")", sizeof(pwin) - 1);
00144 return ERR_OVERFLOW;
00145 }
00146 bufsize = sizeof(pwv);
00147 apr_password_get("Re-type new password: ", pwv, &bufsize);
00148 if (strcmp(pwin, pwv) != 0) {
00149 apr_cpystrn(record, "password verification error", (rlen - 1));
00150 return ERR_PWMISMATCH;
00151 }
00152 pw = pwin;
00153 memset(pwv, '\0', sizeof(pwin));
00154 }
00155 switch (alg) {
00156
00157 case ALG_APSHA:
00158
00159 apr_sha1_base64(pw,strlen(pw),cpw);
00160 break;
00161
00162 case ALG_APMD5:
00163 (void) srand((int) time((time_t *) NULL));
00164 to64(&salt[0], rand(), 8);
00165 salt[8] = '\0';
00166
00167 apr_md5_encode((const char *)pw, (const char *)salt,
00168 cpw, sizeof(cpw));
00169 break;
00170
00171 case ALG_PLAIN:
00172
00173 apr_cpystrn(cpw,pw,sizeof(cpw));
00174 break;
00175
00176 #if !(defined(WIN32) || defined(NETWARE))
00177 case ALG_CRYPT:
00178 default:
00179 (void) srand((int) time((time_t *) NULL));
00180 to64(&salt[0], rand(), 8);
00181 salt[8] = '\0';
00182
00183 apr_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1);
00184 break;
00185 #endif
00186 }
00187 memset(pw, '\0', strlen(pw));
00188
00189
00190
00191
00192
00193 if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) {
00194 apr_cpystrn(record, "resultant record too long", (rlen - 1));
00195 return ERR_OVERFLOW;
00196 }
00197 strcpy(record, user);
00198 strcat(record, ":");
00199 strcat(record, cpw);
00200 strcat(record, "\n");
00201 return 0;
00202 }
00203
00204 static void usage(void)
00205 {
00206 apr_file_printf(errfile, "Usage:\n");
00207 apr_file_printf(errfile, "\thtpasswd [-cmdpsD] passwordfile username\n");
00208 apr_file_printf(errfile, "\thtpasswd -b[cmdpsD] passwordfile username "
00209 "password\n\n");
00210 apr_file_printf(errfile, "\thtpasswd -n[mdps] username\n");
00211 apr_file_printf(errfile, "\thtpasswd -nb[mdps] username password\n");
00212 apr_file_printf(errfile, " -c Create a new file.\n");
00213 apr_file_printf(errfile, " -n Don't update file; display results on "
00214 "stdout.\n");
00215 apr_file_printf(errfile, " -m Force MD5 encryption of the password"
00216 #if defined(WIN32) || defined(TPF) || defined(NETWARE)
00217 " (default)"
00218 #endif
00219 ".\n");
00220 apr_file_printf(errfile, " -d Force CRYPT encryption of the password"
00221 #if (!(defined(WIN32) || defined(TPF) || defined(NETWARE)))
00222 " (default)"
00223 #endif
00224 ".\n");
00225 apr_file_printf(errfile, " -p Do not encrypt the password (plaintext).\n");
00226 apr_file_printf(errfile, " -s Force SHA encryption of the password.\n");
00227 apr_file_printf(errfile, " -b Use the password from the command line "
00228 "rather than prompting for it.\n");
00229 apr_file_printf(errfile, " -D Delete the specified user.\n");
00230 apr_file_printf(errfile,
00231 "On Windows, NetWare and TPF systems the '-m' flag is used by "
00232 "default.\n");
00233 apr_file_printf(errfile,
00234 "On all other systems, the '-p' flag will probably not work.\n");
00235 exit(ERR_SYNTAX);
00236 }
00237
00238
00239
00240
00241
00242 static int accessible(apr_pool_t *pool, char *fname, int mode)
00243 {
00244 apr_file_t *f = NULL;
00245
00246 if (apr_file_open(&f, fname, mode, APR_OS_DEFAULT, pool) != APR_SUCCESS) {
00247 return 0;
00248 }
00249 apr_file_close(f);
00250 return 1;
00251 }
00252
00253
00254
00255
00256 static int exists(char *fname, apr_pool_t *pool)
00257 {
00258 apr_finfo_t sbuf;
00259 apr_status_t check;
00260
00261 check = apr_stat(&sbuf, fname, APR_FINFO_TYPE, pool);
00262 return ((check || sbuf.filetype != APR_REG) ? 0 : 1);
00263 }
00264
00265 static void terminate(void)
00266 {
00267 apr_terminate();
00268 #ifdef NETWARE
00269 pressanykey();
00270 #endif
00271 }
00272
00273 static void check_args(apr_pool_t *pool, int argc, const char *const argv[],
00274 int *alg, int *mask, char **user, char **pwfilename,
00275 char **password)
00276 {
00277 const char *arg;
00278 int args_left = 2;
00279 int i;
00280
00281
00282
00283
00284
00285
00286 if (argc < 3) {
00287 usage();
00288 }
00289
00290
00291
00292
00293
00294 for (i = 1; i < argc; i++) {
00295 arg = argv[i];
00296 if (*arg != '-') {
00297 break;
00298 }
00299 while (*++arg != '\0') {
00300 if (*arg == 'c') {
00301 *mask |= APHTP_NEWFILE;
00302 }
00303 else if (*arg == 'n') {
00304 *mask |= APHTP_NOFILE;
00305 args_left--;
00306 }
00307 else if (*arg == 'm') {
00308 *alg = ALG_APMD5;
00309 }
00310 else if (*arg == 's') {
00311 *alg = ALG_APSHA;
00312 }
00313 else if (*arg == 'p') {
00314 *alg = ALG_PLAIN;
00315 }
00316 else if (*arg == 'd') {
00317 *alg = ALG_CRYPT;
00318 }
00319 else if (*arg == 'b') {
00320 *mask |= APHTP_NONINTERACTIVE;
00321 args_left++;
00322 }
00323 else if (*arg == 'D') {
00324 *mask |= APHTP_DELUSER;
00325 }
00326 else {
00327 usage();
00328 }
00329 }
00330 }
00331
00332 if ((*mask & APHTP_NEWFILE) && (*mask & APHTP_NOFILE)) {
00333 apr_file_printf(errfile, "%s: -c and -n options conflict\n", argv[0]);
00334 exit(ERR_SYNTAX);
00335 }
00336 if ((*mask & APHTP_NEWFILE) && (*mask & APHTP_DELUSER)) {
00337 apr_file_printf(errfile, "%s: -c and -D options conflict\n", argv[0]);
00338 exit(ERR_SYNTAX);
00339 }
00340 if ((*mask & APHTP_NOFILE) && (*mask & APHTP_DELUSER)) {
00341 apr_file_printf(errfile, "%s: -n and -D options conflict\n", argv[0]);
00342 exit(ERR_SYNTAX);
00343 }
00344
00345
00346
00347
00348
00349 if ((argc - i) != args_left) {
00350 usage();
00351 }
00352
00353 if (*mask & APHTP_NOFILE) {
00354 i--;
00355 }
00356 else {
00357 if (strlen(argv[i]) > (APR_PATH_MAX - 1)) {
00358 apr_file_printf(errfile, "%s: filename too long\n", argv[0]);
00359 exit(ERR_OVERFLOW);
00360 }
00361 *pwfilename = apr_pstrdup(pool, argv[i]);
00362 if (strlen(argv[i + 1]) > (MAX_STRING_LEN - 1)) {
00363 apr_file_printf(errfile, "%s: username too long (> %d)\n",
00364 argv[0], MAX_STRING_LEN - 1);
00365 exit(ERR_OVERFLOW);
00366 }
00367 }
00368 *user = apr_pstrdup(pool, argv[i + 1]);
00369 if ((arg = strchr(*user, ':')) != NULL) {
00370 apr_file_printf(errfile, "%s: username contains illegal "
00371 "character '%c'\n", argv[0], *arg);
00372 exit(ERR_BADUSER);
00373 }
00374 if (*mask & APHTP_NONINTERACTIVE) {
00375 if (strlen(argv[i + 2]) > (MAX_STRING_LEN - 1)) {
00376 apr_file_printf(errfile, "%s: password too long (> %d)\n",
00377 argv[0], MAX_STRING_LEN);
00378 exit(ERR_OVERFLOW);
00379 }
00380 *password = apr_pstrdup(pool, argv[i + 2]);
00381 }
00382 }
00383
00384
00385
00386
00387
00388 int main(int argc, const char * const argv[])
00389 {
00390 apr_file_t *fpw = NULL;
00391 char record[MAX_STRING_LEN];
00392 char line[MAX_STRING_LEN];
00393 char *password = NULL;
00394 char *pwfilename = NULL;
00395 char *user = NULL;
00396 char tn[] = "htpasswd.tmp.XXXXXX";
00397 char *dirname;
00398 char *scratch, cp[MAX_STRING_LEN];
00399 int found = 0;
00400 int i;
00401 int alg = ALG_CRYPT;
00402 int mask = 0;
00403 apr_pool_t *pool;
00404 int existing_file = 0;
00405 #if APR_CHARSET_EBCDIC
00406 apr_status_t rv;
00407 apr_xlate_t *to_ascii;
00408 #endif
00409
00410 apr_app_initialize(&argc, &argv, NULL);
00411 atexit(terminate);
00412 apr_pool_create(&pool, NULL);
00413 apr_file_open_stderr(&errfile, pool);
00414
00415 #if APR_CHARSET_EBCDIC
00416 rv = apr_xlate_open(&to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, pool);
00417 if (rv) {
00418 apr_file_printf(errfile, "apr_xlate_open(to ASCII)->%d\n", rv);
00419 exit(1);
00420 }
00421 rv = apr_SHA1InitEBCDIC(to_ascii);
00422 if (rv) {
00423 apr_file_printf(errfile, "apr_SHA1InitEBCDIC()->%d\n", rv);
00424 exit(1);
00425 }
00426 rv = apr_MD5InitEBCDIC(to_ascii);
00427 if (rv) {
00428 apr_file_printf(errfile, "apr_MD5InitEBCDIC()->%d\n", rv);
00429 exit(1);
00430 }
00431 #endif
00432
00433 check_args(pool, argc, argv, &alg, &mask, &user, &pwfilename, &password);
00434
00435
00436 #if defined(WIN32) || defined(NETWARE)
00437 if (alg == ALG_CRYPT) {
00438 alg = ALG_APMD5;
00439 apr_file_printf(errfile, "Automatically using MD5 format.\n");
00440 }
00441 #endif
00442
00443 #if (!(defined(WIN32) || defined(TPF) || defined(NETWARE)))
00444 if (alg == ALG_PLAIN) {
00445 apr_file_printf(errfile,"Warning: storing passwords as plain text "
00446 "might just not work on this platform.\n");
00447 }
00448 #endif
00449
00450
00451
00452
00453 if (!(mask & APHTP_NOFILE)) {
00454 existing_file = exists(pwfilename, pool);
00455 if (existing_file) {
00456
00457
00458
00459 if (!accessible(pool, pwfilename, APR_READ | APR_APPEND)) {
00460 apr_file_printf(errfile, "%s: cannot open file %s for "
00461 "read/write access\n", argv[0], pwfilename);
00462 exit(ERR_FILEPERM);
00463 }
00464 }
00465 else {
00466
00467
00468
00469 if (!(mask & APHTP_NEWFILE)) {
00470 apr_file_printf(errfile,
00471 "%s: cannot modify file %s; use '-c' to create it\n",
00472 argv[0], pwfilename);
00473 exit(ERR_FILEPERM);
00474 }
00475
00476
00477
00478 if (!accessible(pool, pwfilename, APR_CREATE | APR_WRITE)) {
00479 apr_file_printf(errfile, "%s: cannot create file %s\n",
00480 argv[0], pwfilename);
00481 exit(ERR_FILEPERM);
00482 }
00483 }
00484 }
00485
00486
00487
00488
00489
00490
00491
00492
00493 if (!(mask & APHTP_DELUSER)) {
00494 i = mkrecord(user, record, sizeof(record) - 1,
00495 password, alg);
00496 if (i != 0) {
00497 apr_file_printf(errfile, "%s: %s\n", argv[0], record);
00498 exit(i);
00499 }
00500 if (mask & APHTP_NOFILE) {
00501 printf("%s\n", record);
00502 exit(0);
00503 }
00504 }
00505
00506
00507
00508
00509
00510 if (apr_temp_dir_get((const char**)&dirname, pool) != APR_SUCCESS) {
00511 apr_file_printf(errfile, "%s: could not determine temp dir\n",
00512 argv[0]);
00513 exit(ERR_FILEPERM);
00514 }
00515 dirname = apr_psprintf(pool, "%s/%s", dirname, tn);
00516
00517 if (apr_file_mktemp(&ftemp, dirname, 0, pool) != APR_SUCCESS) {
00518 apr_file_printf(errfile, "%s: unable to create temporary file %s\n",
00519 argv[0], dirname);
00520 exit(ERR_FILEPERM);
00521 }
00522
00523
00524
00525
00526
00527 if (existing_file && !(mask & APHTP_NEWFILE)) {
00528 if (apr_file_open(&fpw, pwfilename, APR_READ | APR_BUFFERED,
00529 APR_OS_DEFAULT, pool) != APR_SUCCESS) {
00530 apr_file_printf(errfile, "%s: unable to read file %s\n",
00531 argv[0], pwfilename);
00532 exit(ERR_FILEPERM);
00533 }
00534 while (apr_file_gets(line, sizeof(line), fpw) == APR_SUCCESS) {
00535 char *colon;
00536
00537 strcpy(cp, line);
00538 scratch = cp;
00539 while (apr_isspace(*scratch)) {
00540 ++scratch;
00541 }
00542
00543 if (!*scratch || (*scratch == '#')) {
00544 putline(ftemp, line);
00545 continue;
00546 }
00547
00548
00549
00550 colon = strchr(scratch, ':');
00551 if (colon != NULL) {
00552 *colon = '\0';
00553 }
00554 else {
00555
00556
00557
00558
00559
00560 apr_file_printf(errfile, "\n%s: The file %s does not appear "
00561 "to be a valid htpasswd file.\n",
00562 argv[0], pwfilename);
00563 apr_file_close(fpw);
00564 exit(ERR_INVALID);
00565 }
00566 if (strcmp(user, scratch) != 0) {
00567 putline(ftemp, line);
00568 continue;
00569 }
00570 else {
00571 if (!(mask & APHTP_DELUSER)) {
00572
00573
00574
00575 apr_file_printf(errfile, "Updating ");
00576 putline(ftemp, record);
00577 found++;
00578 }
00579 else {
00580
00581
00582
00583 apr_file_printf(errfile, "Deleting ");
00584 found++;
00585 }
00586 }
00587 }
00588 apr_file_close(fpw);
00589 }
00590 if (!found && !(mask & APHTP_DELUSER)) {
00591 apr_file_printf(errfile, "Adding ");
00592 putline(ftemp, record);
00593 }
00594 else if (!found && (mask & APHTP_DELUSER)) {
00595 apr_file_printf(errfile, "User %s not found\n", user);
00596 exit(0);
00597 }
00598 apr_file_printf(errfile, "password for user %s\n", user);
00599
00600
00601
00602 if (apr_file_copy(dirname, pwfilename, APR_FILE_SOURCE_PERMS, pool) !=
00603 APR_SUCCESS) {
00604 apr_file_printf(errfile, "%s: unable to update file %s\n",
00605 argv[0], pwfilename);
00606 exit(ERR_FILEPERM);
00607 }
00608 apr_file_close(ftemp);
00609 return 0;
00610 }