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

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