[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.11

1.11    ! deraadt     1: /*     $OpenBSD: htpasswd.c,v 1.10 2014/03/24 20:33:01 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.7       florian    39:        fprintf(stderr, "usage:\t%s [file] login\n", __progname);
1.10      florian    40:        fprintf(stderr, "\t%s -I [file]\n", __progname);
1.1       florian    41:        exit(1);
                     42: }
                     43:
1.6       florian    44: #define MAXNAG 5
                     45: int nagcount;
                     46:
1.1       florian    47: int
                     48: main(int argc, char** argv)
                     49: {
                     50:        FILE *in, *out;
                     51:        size_t linesize;
                     52:        ssize_t linelen;
1.2       florian    53:        mode_t old_umask;
1.7       florian    54:        int c, fd, loginlen, batch;
1.9       florian    55:        char hash[_PASSWORD_LEN], *line, *login, pass[1024], pass2[1024];
1.1       florian    56:        char salt[_PASSWORD_LEN], tmpl[sizeof("/tmp/htpasswd-XXXXXXXXXX")];
1.7       florian    57:        char *tok;
1.9       florian    58:        const char *file;
1.1       florian    59:
                     60:        file = NULL;
                     61:        login = NULL;
                     62:        in = NULL;
                     63:        out = NULL;
                     64:        line = NULL;
                     65:        linesize = 0;
1.7       florian    66:        batch = 0;
1.1       florian    67:
1.10      florian    68:        while ((c = getopt(argc, argv, "I")) != -1) {
1.7       florian    69:                switch (c) {
1.10      florian    70:                case 'I':
1.11    ! deraadt    71:                        batch = 1;
1.7       florian    72:                        break;
                     73:                default:
                     74:                        usage();
                     75:                        /* NOT REACHED */
                     76:                        break;
                     77:                }
                     78:        }
                     79:
                     80:        argc -= optind;
                     81:        argv += optind;
                     82:
                     83:        if (batch) {
                     84:                if (argc == 1)
                     85:                        file = argv[0];
                     86:                else if (argc > 1)
                     87:                        usage();
                     88:                if ((linelen = getline(&line, &linesize, stdin)) == -1)
1.9       florian    89:                        err(1, "cannot read login:password from stdin");
1.7       florian    90:                line[linelen-1] = '\0';
                     91:
                     92:                if ((tok = strstr(line, ":")) == NULL)
1.9       florian    93:                        errx(1, "cannot find ':' in input");
1.7       florian    94:                *tok++ = '\0';
                     95:
                     96:                if ((loginlen = asprintf(&login, "%s:", line)) == -1)
1.1       florian    97:                        err(1, "asprintf");
                     98:
1.7       florian    99:                if (strlcpy(pass, tok, sizeof(pass)) >= sizeof(pass))
1.9       florian   100:                        errx(1, "password too long");
1.7       florian   101:        } else {
                    102:
                    103:                switch (argc) {
                    104:                case 1:
                    105:                        if ((loginlen = asprintf(&login, "%s:", argv[0])) == -1)
                    106:                                err(1, "asprintf");
                    107:                        break;
                    108:                case 2:
                    109:                        file = argv[0];
                    110:                        if ((loginlen = asprintf(&login, "%s:", argv[1])) == -1)
                    111:                                err(1, "asprintf");
                    112:                        break;
                    113:                default:
                    114:                        usage();
                    115:                        /* NOT REACHED */
                    116:                        break;
                    117:                }
                    118:
                    119:                if (!readpassphrase("Password: ", pass, sizeof(pass),
                    120:                    RPP_ECHO_OFF))
                    121:                        err(1, "unable to read password");
                    122:                if (!readpassphrase("Retype Password: ", pass2, sizeof(pass2),
                    123:                    RPP_ECHO_OFF)) {
                    124:                        explicit_bzero(pass, sizeof(pass));
                    125:                        err(1, "unable to read password");
                    126:                }
                    127:                if (strcmp(pass, pass2) != 0) {
                    128:                        explicit_bzero(pass, sizeof(pass));
                    129:                        explicit_bzero(pass2, sizeof(pass2));
                    130:                        errx(1, "passwords don't match");
                    131:                }
                    132:
1.1       florian   133:                explicit_bzero(pass2, sizeof(pass2));
                    134:        }
                    135:
                    136:        if (strlcpy(salt, bcrypt_gensalt(8), sizeof(salt)) >= sizeof(salt))
1.9       florian   137:                errx(1, "salt too long");
1.1       florian   138:        if (strlcpy(hash, bcrypt(pass, salt), sizeof(hash)) >= sizeof(hash))
1.9       florian   139:                errx(1, "hash too long");
1.1       florian   140:        explicit_bzero(pass, sizeof(pass));
                    141:
                    142:        if (file == NULL)
                    143:                printf("%s%s\n", login, hash);
                    144:        else {
                    145:                if ((in = fopen(file, "r+")) == NULL) {
                    146:                        if (errno == ENOENT) {
1.2       florian   147:                                old_umask = umask(S_IXUSR|
                    148:                                    S_IWGRP|S_IRGRP|S_IXGRP|
                    149:                                    S_IWOTH|S_IROTH|S_IXOTH);
1.1       florian   150:                                if ((out = fopen(file, "w")) == NULL)
                    151:                                        err(1, "cannot open password file for"
                    152:                                            " reading or writing");
1.2       florian   153:                                umask(old_umask);
1.1       florian   154:                        } else
1.4       benno     155:                                err(1, "cannot open password file for"
                    156:                                        " reading or writing");
1.5       florian   157:                } else
                    158:                        if (flock(fileno(in), LOCK_EX|LOCK_NB) == -1)
                    159:                                errx(1, "cannot lock password file");
                    160:
1.1       florian   161:                /* file already exits, copy content and filter login out */
                    162:                if (out == NULL) {
                    163:                        strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
                    164:                        if ((fd = mkstemp(tmpl)) == -1)
                    165:                                err(1, "mkstemp");
                    166:
                    167:                        if ((out = fdopen(fd, "w+")) == NULL)
                    168:                                err(1, "cannot open tempfile");
                    169:
                    170:                        while ((linelen = getline(&line, &linesize, in))
                    171:                            != -1) {
                    172:                                if (strncmp(line, login, loginlen) != 0) {
                    173:                                        if (fprintf(out, "%s", line) == -1)
1.9       florian   174:                                                errx(1, "cannot write to temp "
1.1       florian   175:                                                    "file");
                    176:                                        nag(line);
                    177:                                }
                    178:                        }
                    179:                }
                    180:                if (fprintf(out, "%s%s\n", login, hash) == -1)
1.9       florian   181:                        errx(1, "cannot write new password hash");
1.1       florian   182:
                    183:                /* file already exists, overwrite it */
                    184:                if (in != NULL) {
                    185:                        if (fseek(in, 0, SEEK_SET) == -1)
                    186:                                err(1, "cannot seek in password file");
1.4       benno     187:                        if (fseek(out, 0, SEEK_SET) == -1)
                    188:                                err(1, "cannot seek in temp file");
1.1       florian   189:                        if (ftruncate(fileno(in), 0) == -1)
                    190:                                err(1, "cannot truncate password file");
                    191:                        while ((linelen = getline(&line, &linesize, out))
                    192:                            != -1)
                    193:                                if (fprintf(in, "%s", line) == -1)
1.9       florian   194:                                        errx(1, "cannot write to password "
                    195:                                            "file");
1.1       florian   196:                        if (fclose(in) == EOF)
                    197:                                err(1, "cannot close password file");
                    198:                }
                    199:                if (fclose(out) == EOF) {
                    200:                        if (in != NULL)
                    201:                                err(1, "cannot close temp file");
                    202:                        else
                    203:                                err(1, "cannot close password file");
                    204:                }
                    205:                if (in != NULL && unlink(tmpl) == -1)
                    206:                        err(1, "cannot delete temp file (%s)", tmpl);
                    207:        }
1.6       florian   208:        if (nagcount >= MAXNAG)
1.9       florian   209:                warnx("%d more logins not using bcryt.", nagcount - MAXNAG);
1.1       florian   210:        exit(0);
                    211: }
                    212:
                    213: void
                    214: nag(char* line)
                    215: {
1.9       florian   216:        const char *tok;
1.1       florian   217:        if (strtok(line, ":") != NULL)
                    218:                if ((tok = strtok(NULL, ":")) != NULL)
                    219:                        if (strncmp(tok, "$2a$", 4) != 0 &&
1.6       florian   220:                             strncmp(tok, "$2b$", 4) != 0) {
                    221:                                nagcount++;
                    222:                                if (nagcount <= MAXNAG)
1.9       florian   223:                                        warnx("%s doesn't use bcrypt."
                    224:                                            " Update the password.", line);
1.6       florian   225:                        }
1.1       florian   226: }