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

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