xref: /netbsd/usr.bin/pwhash/pwhash.c (revision 6605a399)
1 /*	$NetBSD: pwhash.c,v 1.16 2019/10/21 02:36:48 jhigh Exp $	*/
2 /*	$OpenBSD: encrypt.c,v 1.16 2002/02/16 21:27:45 millert Exp $	*/
3 
4 /*
5  * Copyright (c) 1996, Jason Downs.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 #include <sys/cdefs.h>
29 
30 #ifndef lint
31 __RCSID("$NetBSD: pwhash.c,v 1.16 2019/10/21 02:36:48 jhigh Exp $");
32 #endif
33 
34 #include <sys/types.h>
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <pwd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <limits.h>
44 #include <login_cap.h>
45 #include <util.h>
46 
47 /*
48  * Very simple little program, for encrypting passwords from the command
49  * line.  Useful for scripts and such.
50  */
51 
52 #define DO_MAKEKEY 0
53 #define DO_DES     1
54 #define DO_MD5     2
55 #define DO_BLF     3
56 #define DO_SHA1	   4
57 
58 #ifdef HAVE_ARGON2
59 #define DO_ARGON2  5
60 /*
61  * Argon2 variant may be specified in /etc/passwd.conf
62  * If not found, default to ARGON2_DEFAULT_VARIANT_STR
63  * acceptable values are: argon2i, argon2d, argon2id
64  */
65 #define ARGON2_DEFAULT_VARIANT_STR	"argon2id"
66 #endif /* HAVE_ARGON2 */
67 
68 __dead static void
usage(void)69 usage(void)
70 {
71 
72 	(void)fprintf(stderr,
73 #ifdef HAVE_ARGON2
74 	    "Usage: %s [-km] [-A variant[,params]] [-b rounds] [-S rounds] [-s salt] [-p | string]\n",
75 #else
76 	    "Usage: %s [-km] [-b rounds] [-S rounds] [-s salt] [-p | string]\n",
77 #endif /* HAVE_ARGON2 */
78 	    getprogname());
79 	exit(1);
80 }
81 
82 static char *
trim(char * line)83 trim(char *line)
84 {
85 	char *ptr;
86 
87 	for (ptr = &line[strlen(line) - 1]; ptr > line; ptr--) {
88 		if (!isspace((unsigned char)*ptr))
89 			break;
90 	}
91 	ptr[1] = '\0';
92 
93 	for (ptr = line; *ptr && isspace((unsigned char)*ptr); ptr++)
94 		continue;
95 
96 	return ptr;
97 }
98 
99 static void
print_passwd(char * string,int operation,const char * extra)100 print_passwd(char *string, int operation, const char *extra)
101 {
102 	char buf[_PASSWORD_LEN];
103 	char option[LINE_MAX], *key, *opt;
104 	int error = 0;
105 	const char *salt = buf;
106 
107 	switch(operation) {
108 	case DO_MAKEKEY:
109 		/*
110 		 * makekey mode: parse string into separate DES key and salt.
111 		 */
112 		if (strlen(string) != 10) {
113 			/* To be compatible... */
114 			error = EFTYPE;
115 			break;
116 		}
117 		salt = &string[8];
118 		break;
119 
120 	case DO_MD5:
121 		error = pw_gensalt(buf, _PASSWORD_LEN, "md5", extra);
122 		break;
123 
124 	case DO_SHA1:
125 		error = pw_gensalt(buf, _PASSWORD_LEN, "sha1", extra);
126 		break;
127 
128 	case DO_BLF:
129 		error = pw_gensalt(buf, _PASSWORD_LEN, "blowfish", extra);
130 		break;
131 
132 	case DO_DES:
133 		salt = extra;
134 		break;
135 
136 #ifdef HAVE_ARGON2
137 	case DO_ARGON2:
138 		/* pwhash -A <variant>[,param]* */
139 		/* param
140 		 *    m=<m_cost>
141 		 *    t=<t_cost>
142 		 *    p=<threads>
143 		 */
144 		snprintf(option, sizeof(option), "%s", extra);
145 		opt = option;
146 		key = strsep(&opt, ",");
147 		error = pw_gensalt(buf, _PASSWORD_LEN, key, opt);
148 		break;
149 #endif /* HAVE_ARGON2 */
150 
151 	default:
152 		pw_getconf(option, sizeof(option), "default", "localcipher");
153 		opt = option;
154 		key = strsep(&opt, ",");
155 		error = pw_gensalt(buf, _PASSWORD_LEN, key, opt);
156 		break;
157 	}
158 
159 	if (error)
160 		err(1, "Cannot generate salt");
161 
162 	(void)fputs(crypt(string, salt), stdout);
163 }
164 
165 int
main(int argc,char ** argv)166 main(int argc, char **argv)
167 {
168 	int opt;
169 	int operation = -1;
170 	int prompt = 0;
171 	const char *extra = NULL;	/* Store salt or number of rounds */
172 
173 	setprogname(argv[0]);
174 
175 	if (strcmp(getprogname(), "makekey") == 0)
176 		operation = DO_MAKEKEY;
177 
178 #ifdef HAVE_ARGON2
179 	while ((opt = getopt(argc, argv, "kmpS:s:b:A:")) != -1) {
180 #else
181 	while ((opt = getopt(argc, argv, "kmpS:s:b:")) != -1) {
182 #endif /* HAVE_ARGON2 */
183 		switch (opt) {
184 		case 'k':                       /* Stdin/Stdout Unix crypt */
185 			if (operation != -1 || prompt)
186 				usage();
187 			operation = DO_MAKEKEY;
188 			break;
189 
190 		case 'm':                       /* MD5 password hash */
191 			if (operation != -1)
192 				usage();
193 			operation = DO_MD5;
194 			extra = NULL;
195 			break;
196 
197 		case 'p':
198 			if (operation == DO_MAKEKEY)
199 				usage();
200 			prompt = 1;
201 			break;
202 
203 		case 'S':                       /* SHA1 password hash */
204 			if (operation != -1)
205 				usage();
206 			operation = DO_SHA1;
207 			extra = optarg;
208 			break;
209 
210 		case 's':                       /* Unix crypt (DES) */
211 			if (operation != -1 || optarg[0] == '$')
212 				usage();
213 			operation = DO_DES;
214 			extra = optarg;
215 			break;
216 
217 		case 'b':                       /* Blowfish password hash */
218 			if (operation != -1)
219 				usage();
220 			operation = DO_BLF;
221 			extra = optarg;
222 			break;
223 
224 #ifdef HAVE_ARGON2
225 		case 'A':                       /* Argon2 password hash */
226 			if (operation != -1)
227 				usage();
228 			operation = DO_ARGON2;
229 			extra = optarg;
230 			break;
231 #endif /* HAVE_ARGON2 */
232 
233 		default:
234 			usage();
235 		}
236 	}
237 
238 	if (((argc - optind) < 1) || operation == DO_MAKEKEY) {
239 		char line[LINE_MAX], *string;
240 
241 		if (prompt) {
242 			string = getpass("Enter string: ");
243 			print_passwd(string, operation, extra);
244 			(void)fputc('\n', stdout);
245 		} else {
246 			/* Encrypt stdin to stdout. */
247 			while (!feof(stdin) &&
248 			    (fgets(line, sizeof(line), stdin) != NULL)) {
249 				/* Kill the whitesapce. */
250 				string = trim(line);
251 				if (*string == '\0')
252 					continue;
253 
254 				print_passwd(string, operation, extra);
255 
256 				if (operation == DO_MAKEKEY) {
257 					(void)fflush(stdout);
258 					break;
259 				}
260 				(void)fputc('\n', stdout);
261 			}
262 		}
263 	} else {
264 		char *string;
265 
266 		/* can't combine -p with a supplied string */
267 		if (prompt)
268 			usage();
269 
270 		/* Perhaps it isn't worth worrying about, but... */
271 		if ((string = strdup(argv[optind])) == NULL)
272 			err(1, NULL);
273 		/* Wipe the argument. */
274 		(void)memset(argv[optind], 0, strlen(argv[optind]));
275 
276 		print_passwd(string, operation, extra);
277 
278 		(void)fputc('\n', stdout);
279 
280 		/* Wipe our copy, before we free it. */
281 		(void)memset(string, 0, strlen(string));
282 		free(string);
283 	}
284 	return 0;
285 }
286