xref: /openbsd/usr.bin/encrypt/encrypt.c (revision 91f110e0)
1 /*	$OpenBSD: encrypt.c,v 1.30 2013/11/12 13:54:51 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1996, Jason Downs.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/types.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <pwd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <login_cap.h>
38 #include <limits.h>
39 
40 /*
41  * Very simple little program, for encrypting passwords from the command
42  * line.  Useful for scripts and such.
43  */
44 
45 #define DO_MAKEKEY 0
46 #define DO_DES     1
47 #define DO_MD5     2
48 #define DO_BLF     3
49 
50 extern char *__progname;
51 char buffer[_PASSWORD_LEN];
52 
53 void	usage(void);
54 int	ideal_rounds(void);
55 void	print_passwd(char *, int, void *);
56 
57 void
58 usage(void)
59 {
60 
61 	(void)fprintf(stderr,
62 	    "usage: %s [-km] [-b rounds] [-c class] [-p | string] [-s salt]\n",
63 	    __progname);
64 	exit(1);
65 }
66 
67 /*
68  * Time how long 8 rounds takes to measure this system's performance.
69  * We are aiming for something that takes between 0.25 and 0.5 seconds.
70  */
71 int
72 ideal_rounds(void)
73 {
74 	clock_t before, after;
75 	int r = 8;
76 	char buf[_PASSWORD_LEN];
77 	int duration;
78 
79 	strlcpy(buf, bcrypt_gensalt(r), _PASSWORD_LEN);
80 	before = clock();
81 	crypt("testpassword", buf);
82 	after = clock();
83 
84 	duration = after - before;
85 
86 	/* too quick? slow it down. */
87 	while (duration <= CLOCKS_PER_SEC / 4) {
88 		r += 1;
89 		duration *= 2;
90 	}
91 	/* too slow? speed it up. */
92 	while (duration > CLOCKS_PER_SEC / 2) {
93 		r -= 1;
94 		duration /= 2;
95 	}
96 
97 	return r;
98 }
99 
100 
101 void
102 print_passwd(char *string, int operation, void *extra)
103 {
104 	char msalt[3], *salt, *cryptstr;
105 	login_cap_t *lc;
106 	int pwd_gensalt(char *, int, login_cap_t *, char);
107 	void to64(char *, u_int32_t, int n);
108 
109 	switch(operation) {
110 	case DO_MAKEKEY:
111 		/*
112 		 * makekey mode: parse string into separate DES key and salt.
113 		 */
114 		if (strlen(string) != 10) {
115 			/* To be compatible... */
116 			errx(1, "%s", strerror(EFTYPE));
117 		}
118 		strlcpy(msalt, &string[8], sizeof msalt);
119 		salt = msalt;
120 		break;
121 
122 	case DO_MD5:
123 		strlcpy(buffer, "$1$", sizeof buffer);
124 		to64(&buffer[3], arc4random(), 4);
125 		to64(&buffer[7], arc4random(), 4);
126 		strlcpy(buffer + 11, "$", sizeof buffer - 11);
127 		salt = buffer;
128 		break;
129 
130 	case DO_BLF:
131 		strlcpy(buffer, bcrypt_gensalt(*(int *)extra), _PASSWORD_LEN);
132 		salt = buffer;
133 		break;
134 
135 	case DO_DES:
136 		salt = extra;
137 		break;
138 
139 	default:
140 		if ((lc = login_getclass(extra)) == NULL)
141 			errx(1, "unable to get login class `%s'",
142 			    extra ? (char *)extra : "default");
143 		if (!pwd_gensalt(buffer, _PASSWORD_LEN, lc, 'l'))
144 			errx(1, "can't generate salt");
145 		salt = buffer;
146 		break;
147 	}
148 
149 	if ((cryptstr = crypt(string, salt)) == NULL)
150 		errx(1, "crypt failed");
151 	fputs(cryptstr, stdout);
152 }
153 
154 int
155 main(int argc, char **argv)
156 {
157 	int opt;
158 	int operation = -1;
159 	int prompt = 0;
160 	int rounds;
161 	void *extra = NULL;		/* Store salt or number of rounds */
162 	const char *errstr;
163 
164 	if (strcmp(__progname, "makekey") == 0)
165 		operation = DO_MAKEKEY;
166 
167 	while ((opt = getopt(argc, argv, "kmps:b:c:")) != -1) {
168 		switch (opt) {
169 		case 'k':                       /* Stdin/Stdout Unix crypt */
170 			if (operation != -1 || prompt)
171 				usage();
172 			operation = DO_MAKEKEY;
173 			break;
174 
175 		case 'm':                       /* MD5 password hash */
176 			if (operation != -1)
177 				usage();
178 			operation = DO_MD5;
179 			break;
180 
181 		case 'p':
182 			if (operation == DO_MAKEKEY)
183 				usage();
184 			prompt = 1;
185 			break;
186 
187 		case 's':                       /* Unix crypt (DES) */
188 			if (operation != -1 || optarg[0] == '$')
189 				usage();
190 			operation = DO_DES;
191 			extra = optarg;
192 			break;
193 
194 		case 'b':                       /* Blowfish password hash */
195 			if (operation != -1)
196 				usage();
197 			operation = DO_BLF;
198 			if (strcmp(optarg, "a") == 0)
199 				rounds = ideal_rounds();
200 			else
201 				rounds = strtonum(optarg, 1, INT_MAX, &errstr);
202 			if (errstr != NULL)
203 				errx(1, "%s: %s", errstr, optarg);
204 			extra = &rounds;
205 			break;
206 
207 		case 'c':                       /* user login class */
208 			extra = optarg;
209 			operation = -1;
210 			break;
211 
212 		default:
213 			usage();
214 		}
215 	}
216 
217 	if (((argc - optind) < 1) || operation == DO_MAKEKEY) {
218 		char line[BUFSIZ], *string;
219 
220 		if (prompt) {
221 			if ((string = getpass("Enter string: ")) == NULL)
222 				err(1, "getpass");
223 			print_passwd(string, operation, extra);
224 			(void)fputc('\n', stdout);
225 		} else {
226 			size_t len;
227 			/* Encrypt stdin to stdout. */
228 			while (!feof(stdin) &&
229 			    (fgets(line, sizeof(line), stdin) != NULL)) {
230 			    	len = strlen(line);
231 				if (len == 0 || line[0] == '\n')
232 					continue;
233 				if (line[len - 1] == '\n')
234                      			line[len - 1] = '\0';
235 
236 				print_passwd(line, operation, extra);
237 
238 				if (operation == DO_MAKEKEY) {
239 					fflush(stdout);
240 					break;
241 				}
242 				(void)fputc('\n', stdout);
243 			}
244 		}
245 	} else {
246 		char *string;
247 
248 		/* can't combine -p with a supplied string */
249 		if (prompt)
250 			usage();
251 
252 		/* Perhaps it isn't worth worrying about, but... */
253 		if ((string = strdup(argv[optind])) == NULL)
254 			err(1, NULL);
255 		/* Wipe the argument. */
256 		memset(argv[optind], 0, strlen(argv[optind]));
257 
258 		print_passwd(string, operation, extra);
259 
260 		(void)fputc('\n', stdout);
261 
262 		/* Wipe our copy, before we free it. */
263 		memset(string, 0, strlen(string));
264 		free(string);
265 	}
266 	exit(0);
267 }
268