GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/htpasswd/htpasswd.c Lines: 0 109 0.0 %
Date: 2016-12-06 Branches: 0 103 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: htpasswd.c,v 1.15 2015/11/05 20:07:15 florian Exp $ */
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 <fcntl.h>
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
{
39
	fprintf(stderr, "usage:\t%s [file] login\n", __progname);
40
	fprintf(stderr, "\t%s -I [file]\n", __progname);
41
	exit(1);
42
}
43
44
#define MAXNAG 5
45
int nagcount;
46
47
int
48
main(int argc, char** argv)
49
{
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;
57
	ssize_t linelen;
58
	mode_t old_umask;
59
60
	if (pledge("stdio rpath wpath cpath flock tmppath tty", NULL) == -1)
61
		err(1, "pledge");
62
63
	while ((c = getopt(argc, argv, "I")) != -1) {
64
		switch (c) {
65
		case 'I':
66
			batch = 1;
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
		else if (pledge("stdio wpath cpath rpath", NULL) == -1)
84
			err(1, "pledge");
85
86
		if ((linelen = getline(&line, &linesize, stdin)) == -1)
87
			err(1, "cannot read login:password from stdin");
88
		line[linelen-1] = '\0';
89
90
		if ((tok = strstr(line, ":")) == NULL)
91
			errx(1, "cannot find ':' in input");
92
		*tok++ = '\0';
93
94
		if ((loginlen = asprintf(&login, "%s:", line)) == -1)
95
			err(1, "asprintf");
96
97
		if (strlcpy(pass, tok, sizeof(pass)) >= sizeof(pass))
98
			errx(1, "password too long");
99
	} else {
100
101
		switch (argc) {
102
		case 1:
103
			if (pledge("stdio tty wpath cpath rpath", NULL) == -1)
104
				err(1, "pledge");
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
133
		explicit_bzero(pass2, sizeof(pass2));
134
	}
135
136
	if (strlcpy(salt, bcrypt_gensalt(8), sizeof(salt)) >= sizeof(salt))
137
		errx(1, "salt too long");
138
	if (strlcpy(hash, bcrypt(pass, salt), sizeof(hash)) >= sizeof(hash))
139
		errx(1, "hash too long");
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) {
147
				old_umask = umask(S_IXUSR|
148
				    S_IWGRP|S_IRGRP|S_IXGRP|
149
				    S_IWOTH|S_IROTH|S_IXOTH);
150
				if ((out = fopen(file, "w")) == NULL)
151
					err(1, "cannot open password file for"
152
					    " reading or writing");
153
				umask(old_umask);
154
			} else
155
				err(1, "cannot open password file for"
156
					" reading or writing");
157
		} else
158
			if (flock(fileno(in), LOCK_EX|LOCK_NB) == -1)
159
				errx(1, "cannot lock password file");
160
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)
174
						errx(1, "cannot write to temp "
175
						    "file");
176
					nag(line);
177
				}
178
			}
179
		}
180
		if (fprintf(out, "%s%s\n", login, hash) == -1)
181
			errx(1, "cannot write new password hash");
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");
187
			if (fseek(out, 0, SEEK_SET) == -1)
188
				err(1, "cannot seek in temp file");
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)
194
					errx(1, "cannot write to password "
195
					    "file");
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
	}
208
	if (nagcount >= MAXNAG)
209
		warnx("%d more logins not using bcryt.", nagcount - MAXNAG);
210
	exit(0);
211
}
212
213
void
214
nag(char* line)
215
{
216
	const char *tok;
217
	if (strtok(line, ":") != NULL)
218
		if ((tok = strtok(NULL, ":")) != NULL)
219
			if (strncmp(tok, "$2a$", 4) != 0 &&
220
			     strncmp(tok, "$2b$", 4) != 0) {
221
				nagcount++;
222
				if (nagcount <= MAXNAG)
223
					warnx("%s doesn't use bcrypt."
224
					    " Update the password.", line);
225
			}
226
}