Annotation of src/usr.bin/htpasswd/htpasswd.c, Revision 1.1
1.1 ! florian 1: /* $OpenBSD$ */
! 2: /*
! 3: * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17:
! 18: #include <sys/stat.h>
! 19:
! 20: #include <err.h>
! 21: #include <errno.h>
! 22: #include <limits.h>
! 23: #include <pwd.h>
! 24: #include <readpassphrase.h>
! 25: #include <stdio.h>
! 26: #include <stdlib.h>
! 27: #include <string.h>
! 28: #include <unistd.h>
! 29:
! 30: __dead void usage(void);
! 31: void nag(char*);
! 32:
! 33: extern char *__progname;
! 34:
! 35: __dead void
! 36: usage(void)
! 37: {
! 38: fprintf(stderr, "usage:\t%s [file] login\n", __progname);
! 39: exit(1);
! 40: }
! 41:
! 42: int
! 43: main(int argc, char** argv)
! 44: {
! 45: FILE *in, *out;
! 46: size_t linesize;
! 47: ssize_t linelen;
! 48: int fd, loginlen;
! 49: char hash[_PASSWORD_LEN], *file, *line, *login, pass[1024], pass2[1024];
! 50: char salt[_PASSWORD_LEN], tmpl[sizeof("/tmp/htpasswd-XXXXXXXXXX")];
! 51:
! 52: file = NULL;
! 53: login = NULL;
! 54: in = NULL;
! 55: out = NULL;
! 56: line = NULL;
! 57: linesize = 0;
! 58:
! 59: switch (argc) {
! 60: case 2:
! 61: if ((loginlen = asprintf(&login, "%s:", argv[1])) == -1)
! 62: err(1, "asprintf");
! 63: break;
! 64: case 3:
! 65: file = argv[1];
! 66: if ((loginlen = asprintf(&login, "%s:", argv[2])) == -1)
! 67: err(1, "asprintf");
! 68: break;
! 69: default:
! 70: usage();
! 71: /* NOT REACHED */
! 72: break;
! 73: }
! 74:
! 75: if (!readpassphrase("Password: ", pass, sizeof(pass), RPP_ECHO_OFF))
! 76: err(1, "unable to read password");
! 77: if (!readpassphrase("Retype Password: ", pass2, sizeof(pass2),
! 78: RPP_ECHO_OFF)) {
! 79: explicit_bzero(pass, sizeof(pass));
! 80: err(1, "unable to read password");
! 81: }
! 82: if (strcmp(pass, pass2) != 0) {
! 83: explicit_bzero(pass, sizeof(pass));
! 84: explicit_bzero(pass2, sizeof(pass2));
! 85: errx(1, "passwords don't match");
! 86: }
! 87:
! 88: explicit_bzero(pass2, sizeof(pass2));
! 89: if (strlcpy(salt, bcrypt_gensalt(8), sizeof(salt)) >= sizeof(salt))
! 90: err(1, "salt too long");
! 91: if (strlcpy(hash, bcrypt(pass, salt), sizeof(hash)) >= sizeof(hash))
! 92: err(1, "hash too long");
! 93: explicit_bzero(pass, sizeof(pass));
! 94:
! 95: if (file == NULL)
! 96: printf("%s%s\n", login, hash);
! 97: else {
! 98: if ((in = fopen(file, "r+")) == NULL) {
! 99: if (errno == ENOENT) {
! 100: if ((out = fopen(file, "w")) == NULL)
! 101: err(1, "cannot open password file for"
! 102: " reading or writing");
! 103: if (fchmod(fileno(out), S_IRUSR | S_IWUSR)
! 104: == -1)
! 105: err(1, "cannot chmod new password"
! 106: " file");
! 107: } else
! 108: err(1, "cannot open password file for reading");
! 109: }
! 110: /* file already exits, copy content and filter login out */
! 111: if (out == NULL) {
! 112: strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
! 113: if ((fd = mkstemp(tmpl)) == -1)
! 114: err(1, "mkstemp");
! 115:
! 116: if ((out = fdopen(fd, "w+")) == NULL)
! 117: err(1, "cannot open tempfile");
! 118:
! 119: while ((linelen = getline(&line, &linesize, in))
! 120: != -1) {
! 121: if (strncmp(line, login, loginlen) != 0) {
! 122: if (fprintf(out, "%s", line) == -1)
! 123: err(1, "cannot write to temp "
! 124: "file");
! 125: nag(line);
! 126: }
! 127: }
! 128: }
! 129: if (fprintf(out, "%s%s\n", login, hash) == -1)
! 130: err(1, "cannot write new password hash");
! 131:
! 132: /* file already exists, overwrite it */
! 133: if (in != NULL) {
! 134: if (fseek(in, 0, SEEK_SET) == -1)
! 135: err(1, "cannot seek in password file");
! 136: if (ftruncate(fileno(in), 0) == -1)
! 137: err(1, "cannot truncate password file");
! 138: if (fseek(out, 0, SEEK_SET) == -1)
! 139: err(1, "cannot seek in temp file");
! 140: while ((linelen = getline(&line, &linesize, out))
! 141: != -1)
! 142: if (fprintf(in, "%s", line) == -1)
! 143: err(1, "cannot write to password file");
! 144: if (fclose(in) == EOF)
! 145: err(1, "cannot close password file");
! 146: }
! 147: if (fclose(out) == EOF) {
! 148: if (in != NULL)
! 149: err(1, "cannot close temp file");
! 150: else
! 151: err(1, "cannot close password file");
! 152: }
! 153: if (in != NULL && unlink(tmpl) == -1)
! 154: err(1, "cannot delete temp file (%s)", tmpl);
! 155: }
! 156: exit(0);
! 157: }
! 158:
! 159: void
! 160: nag(char* line)
! 161: {
! 162: char *tok;
! 163: if (strtok(line, ":") != NULL)
! 164: if ((tok = strtok(NULL, ":")) != NULL)
! 165: if (strncmp(tok, "$2a$", 4) != 0 &&
! 166: strncmp(tok, "$2b$", 4) != 0)
! 167: fprintf(stderr, "%s doesn't use bcrypt."
! 168: " Update the password.\n", line);
! 169: }