[BACK]Return to htpasswd.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / htpasswd

Annotation of src/usr.bin/htpasswd/htpasswd.c, Revision 1.6

1.6     ! florian     1: /*     $OpenBSD: htpasswd.c,v 1.5 2014/03/17 22:37:53 florian Exp $ */
1.1       florian     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>
1.5       florian    22: #include <fcntl.h>
1.1       florian    23: #include <limits.h>
                     24: #include <pwd.h>
                     25: #include <readpassphrase.h>
                     26: #include <stdio.h>
                     27: #include <stdlib.h>
                     28: #include <string.h>
                     29: #include <unistd.h>
                     30:
                     31: __dead void    usage(void);
                     32: void           nag(char*);
                     33:
                     34: extern char *__progname;
                     35:
                     36: __dead void
                     37: usage(void)
                     38: {
1.3       florian    39:        fprintf(stderr, "usage: %s [file] login\n", __progname);
1.1       florian    40:        exit(1);
                     41: }
                     42:
1.6     ! florian    43: #define MAXNAG 5
        !            44: int nagcount;
        !            45:
1.1       florian    46: int
                     47: main(int argc, char** argv)
                     48: {
                     49:        FILE *in, *out;
                     50:        size_t linesize;
                     51:        ssize_t linelen;
1.2       florian    52:        mode_t old_umask;
1.1       florian    53:        int fd, loginlen;
                     54:        char hash[_PASSWORD_LEN], *file, *line, *login, pass[1024], pass2[1024];
                     55:        char salt[_PASSWORD_LEN], tmpl[sizeof("/tmp/htpasswd-XXXXXXXXXX")];
                     56:
                     57:        file = NULL;
                     58:        login = NULL;
                     59:        in = NULL;
                     60:        out = NULL;
                     61:        line = NULL;
                     62:        linesize = 0;
                     63:
                     64:        switch (argc) {
                     65:        case 2:
                     66:                if ((loginlen = asprintf(&login, "%s:", argv[1])) == -1)
                     67:                        err(1, "asprintf");
                     68:                break;
                     69:        case 3:
                     70:                file = argv[1];
                     71:                if ((loginlen = asprintf(&login, "%s:", argv[2])) == -1)
                     72:                        err(1, "asprintf");
                     73:                break;
                     74:        default:
                     75:                usage();
                     76:                /* NOT REACHED */
                     77:                break;
                     78:        }
                     79:
                     80:        if (!readpassphrase("Password: ", pass, sizeof(pass), RPP_ECHO_OFF))
                     81:                err(1, "unable to read password");
                     82:        if (!readpassphrase("Retype Password: ", pass2, sizeof(pass2),
                     83:            RPP_ECHO_OFF)) {
                     84:                explicit_bzero(pass, sizeof(pass));
                     85:                err(1, "unable to read password");
                     86:        }
                     87:        if (strcmp(pass, pass2) != 0) {
                     88:                explicit_bzero(pass, sizeof(pass));
                     89:                explicit_bzero(pass2, sizeof(pass2));
                     90:                errx(1, "passwords don't match");
                     91:        }
                     92:
                     93:        explicit_bzero(pass2, sizeof(pass2));
                     94:        if (strlcpy(salt, bcrypt_gensalt(8), sizeof(salt)) >= sizeof(salt))
                     95:                err(1, "salt too long");
                     96:        if (strlcpy(hash, bcrypt(pass, salt), sizeof(hash)) >= sizeof(hash))
                     97:                err(1, "hash too long");
                     98:        explicit_bzero(pass, sizeof(pass));
                     99:
                    100:        if (file == NULL)
                    101:                printf("%s%s\n", login, hash);
                    102:        else {
                    103:                if ((in = fopen(file, "r+")) == NULL) {
                    104:                        if (errno == ENOENT) {
1.2       florian   105:                                old_umask = umask(S_IXUSR|
                    106:                                    S_IWGRP|S_IRGRP|S_IXGRP|
                    107:                                    S_IWOTH|S_IROTH|S_IXOTH);
1.1       florian   108:                                if ((out = fopen(file, "w")) == NULL)
                    109:                                        err(1, "cannot open password file for"
                    110:                                            " reading or writing");
1.2       florian   111:                                umask(old_umask);
1.1       florian   112:                        } else
1.4       benno     113:                                err(1, "cannot open password file for"
                    114:                                        " reading or writing");
1.5       florian   115:                } else
                    116:                        if (flock(fileno(in), LOCK_EX|LOCK_NB) == -1)
                    117:                                errx(1, "cannot lock password file");
                    118:
1.1       florian   119:                /* file already exits, copy content and filter login out */
                    120:                if (out == NULL) {
                    121:                        strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
                    122:                        if ((fd = mkstemp(tmpl)) == -1)
                    123:                                err(1, "mkstemp");
                    124:
                    125:                        if ((out = fdopen(fd, "w+")) == NULL)
                    126:                                err(1, "cannot open tempfile");
                    127:
                    128:                        while ((linelen = getline(&line, &linesize, in))
                    129:                            != -1) {
                    130:                                if (strncmp(line, login, loginlen) != 0) {
                    131:                                        if (fprintf(out, "%s", line) == -1)
                    132:                                                err(1, "cannot write to temp "
                    133:                                                    "file");
                    134:                                        nag(line);
                    135:                                }
                    136:                        }
                    137:                }
                    138:                if (fprintf(out, "%s%s\n", login, hash) == -1)
                    139:                        err(1, "cannot write new password hash");
                    140:
                    141:                /* file already exists, overwrite it */
                    142:                if (in != NULL) {
                    143:                        if (fseek(in, 0, SEEK_SET) == -1)
                    144:                                err(1, "cannot seek in password file");
1.4       benno     145:                        if (fseek(out, 0, SEEK_SET) == -1)
                    146:                                err(1, "cannot seek in temp file");
1.1       florian   147:                        if (ftruncate(fileno(in), 0) == -1)
                    148:                                err(1, "cannot truncate password file");
                    149:                        while ((linelen = getline(&line, &linesize, out))
                    150:                            != -1)
                    151:                                if (fprintf(in, "%s", line) == -1)
                    152:                                        err(1, "cannot write to password file");
                    153:                        if (fclose(in) == EOF)
                    154:                                err(1, "cannot close password file");
                    155:                }
                    156:                if (fclose(out) == EOF) {
                    157:                        if (in != NULL)
                    158:                                err(1, "cannot close temp file");
                    159:                        else
                    160:                                err(1, "cannot close password file");
                    161:                }
                    162:                if (in != NULL && unlink(tmpl) == -1)
                    163:                        err(1, "cannot delete temp file (%s)", tmpl);
                    164:        }
1.6     ! florian   165:        if (nagcount >= MAXNAG)
        !           166:                fprintf(stderr, "%d more logins not using bcryt.\n",
        !           167:                    nagcount - MAXNAG);
1.1       florian   168:        exit(0);
                    169: }
                    170:
                    171: void
                    172: nag(char* line)
                    173: {
                    174:        char *tok;
                    175:        if (strtok(line, ":") != NULL)
                    176:                if ((tok = strtok(NULL, ":")) != NULL)
                    177:                        if (strncmp(tok, "$2a$", 4) != 0 &&
1.6     ! florian   178:                             strncmp(tok, "$2b$", 4) != 0) {
        !           179:                                nagcount++;
        !           180:                                if (nagcount <= MAXNAG)
        !           181:                                        fprintf(stderr, "%s doesn't use bcrypt."
        !           182:                                            " Update the password.\n", line);
        !           183:                        }
1.1       florian   184: }