Annotation of src/usr.bin/htpasswd/htpasswd.c, Revision 1.2
1.2 ! florian 1: /* $OpenBSD: htpasswd.c,v 1.1 2014/03/17 12:49:13 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: {
38: fprintf(stderr, "usage:\t%s [file] login\n", __progname);
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
109: err(1, "cannot open password file for reading");
110: }
111: /* file already exits, copy content and filter login out */
112: if (out == NULL) {
113: strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
114: if ((fd = mkstemp(tmpl)) == -1)
115: err(1, "mkstemp");
116:
117: if ((out = fdopen(fd, "w+")) == NULL)
118: err(1, "cannot open tempfile");
119:
120: while ((linelen = getline(&line, &linesize, in))
121: != -1) {
122: if (strncmp(line, login, loginlen) != 0) {
123: if (fprintf(out, "%s", line) == -1)
124: err(1, "cannot write to temp "
125: "file");
126: nag(line);
127: }
128: }
129: }
130: if (fprintf(out, "%s%s\n", login, hash) == -1)
131: err(1, "cannot write new password hash");
132:
133: /* file already exists, overwrite it */
134: if (in != NULL) {
135: if (fseek(in, 0, SEEK_SET) == -1)
136: err(1, "cannot seek in password file");
137: if (ftruncate(fileno(in), 0) == -1)
138: err(1, "cannot truncate password file");
139: if (fseek(out, 0, SEEK_SET) == -1)
140: err(1, "cannot seek in temp file");
141: while ((linelen = getline(&line, &linesize, out))
142: != -1)
143: if (fprintf(in, "%s", line) == -1)
144: err(1, "cannot write to password file");
145: if (fclose(in) == EOF)
146: err(1, "cannot close password file");
147: }
148: if (fclose(out) == EOF) {
149: if (in != NULL)
150: err(1, "cannot close temp file");
151: else
152: err(1, "cannot close password file");
153: }
154: if (in != NULL && unlink(tmpl) == -1)
155: err(1, "cannot delete temp file (%s)", tmpl);
156: }
157: exit(0);
158: }
159:
160: void
161: nag(char* line)
162: {
163: char *tok;
164: if (strtok(line, ":") != NULL)
165: if ((tok = strtok(NULL, ":")) != NULL)
166: if (strncmp(tok, "$2a$", 4) != 0 &&
167: strncmp(tok, "$2b$", 4) != 0)
168: fprintf(stderr, "%s doesn't use bcrypt."
169: " Update the password.\n", line);
170: }