Annotation of src/usr.bin/htpasswd/htpasswd.c, Revision 1.4
1.4 ! benno 1: /* $OpenBSD: htpasswd.c,v 1.3 2014/03/17 13:54:58 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>
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: {
1.3 florian 38: fprintf(stderr, "usage: %s [file] login\n", __progname);
1.1 florian 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;
1.2 florian 48: mode_t old_umask;
1.1 florian 49: int fd, loginlen;
50: char hash[_PASSWORD_LEN], *file, *line, *login, pass[1024], pass2[1024];
51: char salt[_PASSWORD_LEN], tmpl[sizeof("/tmp/htpasswd-XXXXXXXXXX")];
52:
53: file = NULL;
54: login = NULL;
55: in = NULL;
56: out = NULL;
57: line = NULL;
58: linesize = 0;
59:
60: switch (argc) {
61: case 2:
62: if ((loginlen = asprintf(&login, "%s:", argv[1])) == -1)
63: err(1, "asprintf");
64: break;
65: case 3:
66: file = argv[1];
67: if ((loginlen = asprintf(&login, "%s:", argv[2])) == -1)
68: err(1, "asprintf");
69: break;
70: default:
71: usage();
72: /* NOT REACHED */
73: break;
74: }
75:
76: if (!readpassphrase("Password: ", pass, sizeof(pass), RPP_ECHO_OFF))
77: err(1, "unable to read password");
78: if (!readpassphrase("Retype Password: ", pass2, sizeof(pass2),
79: RPP_ECHO_OFF)) {
80: explicit_bzero(pass, sizeof(pass));
81: err(1, "unable to read password");
82: }
83: if (strcmp(pass, pass2) != 0) {
84: explicit_bzero(pass, sizeof(pass));
85: explicit_bzero(pass2, sizeof(pass2));
86: errx(1, "passwords don't match");
87: }
88:
89: explicit_bzero(pass2, sizeof(pass2));
90: if (strlcpy(salt, bcrypt_gensalt(8), sizeof(salt)) >= sizeof(salt))
91: err(1, "salt too long");
92: if (strlcpy(hash, bcrypt(pass, salt), sizeof(hash)) >= sizeof(hash))
93: err(1, "hash too long");
94: explicit_bzero(pass, sizeof(pass));
95:
96: if (file == NULL)
97: printf("%s%s\n", login, hash);
98: else {
99: if ((in = fopen(file, "r+")) == NULL) {
100: if (errno == ENOENT) {
1.2 florian 101: old_umask = umask(S_IXUSR|
102: S_IWGRP|S_IRGRP|S_IXGRP|
103: S_IWOTH|S_IROTH|S_IXOTH);
1.1 florian 104: if ((out = fopen(file, "w")) == NULL)
105: err(1, "cannot open password file for"
106: " reading or writing");
1.2 florian 107: umask(old_umask);
1.1 florian 108: } else
1.4 ! benno 109: err(1, "cannot open password file for"
! 110: " reading or writing");
1.1 florian 111: }
112: /* file already exits, copy content and filter login out */
113: if (out == NULL) {
114: strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
115: if ((fd = mkstemp(tmpl)) == -1)
116: err(1, "mkstemp");
117:
118: if ((out = fdopen(fd, "w+")) == NULL)
119: err(1, "cannot open tempfile");
120:
121: while ((linelen = getline(&line, &linesize, in))
122: != -1) {
123: if (strncmp(line, login, loginlen) != 0) {
124: if (fprintf(out, "%s", line) == -1)
125: err(1, "cannot write to temp "
126: "file");
127: nag(line);
128: }
129: }
130: }
131: if (fprintf(out, "%s%s\n", login, hash) == -1)
132: err(1, "cannot write new password hash");
133:
134: /* file already exists, overwrite it */
135: if (in != NULL) {
136: if (fseek(in, 0, SEEK_SET) == -1)
137: err(1, "cannot seek in password file");
1.4 ! benno 138: if (fseek(out, 0, SEEK_SET) == -1)
! 139: err(1, "cannot seek in temp file");
1.1 florian 140: if (ftruncate(fileno(in), 0) == -1)
141: err(1, "cannot truncate password file");
142: while ((linelen = getline(&line, &linesize, out))
143: != -1)
144: if (fprintf(in, "%s", line) == -1)
145: err(1, "cannot write to password file");
146: if (fclose(in) == EOF)
147: err(1, "cannot close password file");
148: }
149: if (fclose(out) == EOF) {
150: if (in != NULL)
151: err(1, "cannot close temp file");
152: else
153: err(1, "cannot close password file");
154: }
155: if (in != NULL && unlink(tmpl) == -1)
156: err(1, "cannot delete temp file (%s)", tmpl);
157: }
158: exit(0);
159: }
160:
161: void
162: nag(char* line)
163: {
164: char *tok;
165: if (strtok(line, ":") != NULL)
166: if ((tok = strtok(NULL, ":")) != NULL)
167: if (strncmp(tok, "$2a$", 4) != 0 &&
168: strncmp(tok, "$2b$", 4) != 0)
169: fprintf(stderr, "%s doesn't use bcrypt."
170: " Update the password.\n", line);
171: }