1/*	$NetBSD: pw_gensalt.c,v 1.13 2021/10/20 13:03:29 nia Exp $	*/
2
3/*
4 * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
5 * 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 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *      This product includes software developed by Niels Provos.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * from OpenBSD: pwd_gensalt.c,v 1.9 1998/07/05 21:08:32 provos Exp
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37__RCSID("$NetBSD: pw_gensalt.c,v 1.13 2021/10/20 13:03:29 nia Exp $");
38#endif /* not lint */
39
40#include <sys/syslimits.h>
41#include <sys/types.h>
42
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <limits.h>
47#include <err.h>
48#include <grp.h>
49#include <pwd.h>
50#include <util.h>
51#include <time.h>
52#include <errno.h>
53
54#include "crypt.h"
55
56#ifdef HAVE_ARGON2
57#include <argon2.h>
58#define ARGON2_ARGON2_STR       "argon2"
59#define ARGON2_ARGON2I_STR      "argon2i"
60#define ARGON2_ARGON2D_STR      "argon2d"
61#define ARGON2_ARGON2ID_STR     "argon2id"
62
63crypt_private int
64estimate_argon2_params(argon2_type, uint32_t *, uint32_t *, uint32_t *);
65#endif /* HAVE_ARGON2 */
66
67static const struct pw_salt {
68	const char *name;
69	int (*gensalt)(char *, size_t, const char *);
70} salts[] = {
71	{ "old", __gensalt_old },
72	{ "new", __gensalt_new },
73	{ "newsalt", __gensalt_new },
74	{ "md5", __gensalt_md5 },
75	{ "sha1", __gensalt_sha1 },
76	{ "blowfish", __gensalt_blowfish },
77#ifdef HAVE_ARGON2
78	/* argon2 default to argon2id */
79	{ "argon2", __gensalt_argon2id},
80	{ "argon2id", __gensalt_argon2id},
81	{ "argon2i", __gensalt_argon2i},
82	{ "argon2d", __gensalt_argon2d},
83#endif /* HAVE_ARGON2 */
84	{ NULL, NULL }
85};
86
87crypt_private int
88/*ARGSUSED2*/
89__gensalt_old(char *salt, size_t saltsiz, const char *option)
90{
91	if (saltsiz < 3) {
92		errno = ENOSPC;
93		return -1;
94	}
95	__crypt_to64(&salt[0], arc4random(), 2);
96	salt[2] = '\0';
97	return 0;
98}
99
100crypt_private int
101/*ARGSUSED2*/
102__gensalt_new(char *salt, size_t saltsiz, const char* option)
103{
104	size_t nrounds;
105
106	if (saltsiz < 10) {
107		errno = ENOSPC;
108		return -1;
109	}
110
111	if (getnum(option, &nrounds) == -1)
112		return -1;
113
114	/* Check rounds, 24 bit is max */
115	if (nrounds < 7250)
116		nrounds = 7250;
117	else if (nrounds > 0xffffff)
118		nrounds = 0xffffff;
119	salt[0] = _PASSWORD_EFMT1;
120	__crypt_to64(&salt[1], (uint32_t)nrounds, 4);
121	__crypt_to64(&salt[5], arc4random(), 4);
122	salt[9] = '\0';
123	return 0;
124}
125
126crypt_private int
127/*ARGSUSED2*/
128__gensalt_md5(char *salt, size_t saltsiz, const char *option)
129{
130	if (saltsiz < 13) {  /* $1$8salt$\0 */
131		errno = ENOSPC;
132		return -1;
133	}
134	salt[0] = _PASSWORD_NONDES;
135	salt[1] = '1';
136	salt[2] = '$';
137	__crypt_to64(&salt[3], arc4random(), 4);
138	__crypt_to64(&salt[7], arc4random(), 4);
139	salt[11] = '$';
140	salt[12] = '\0';
141	return 0;
142}
143
144crypt_private int
145__gensalt_sha1(char *salt, size_t saltsiz, const char *option)
146{
147	int n;
148	size_t nrounds;
149
150	if (getnum(option, &nrounds) == -1)
151		return -1;
152	n = snprintf(salt, saltsiz, "%s%u$", SHA1_MAGIC,
153	    __crypt_sha1_iterations(nrounds));
154	/*
155	 * The salt can be up to 64 bytes, but 8
156	 * is considered enough for now.
157	 */
158	if ((size_t)n + 9 >= saltsiz)
159		return 0;
160	__crypt_to64(&salt[n], arc4random(), 4);
161	__crypt_to64(&salt[n + 4], arc4random(), 4);
162	salt[n + 8] = '$';
163	salt[n + 9] = '\0';
164	return 0;
165}
166
167#ifdef HAVE_ARGON2
168static int
169__gensalt_argon2_decode_option(char *dst, size_t dlen,
170    const char *option, argon2_type atype)
171{
172	char *in = 0;
173	char *a = 0;
174	size_t tmp = 0;
175	int error = 0;
176	uint32_t memory = 0;
177	uint32_t time = 0;
178	uint32_t threads = 0;
179
180	memset(dst, 0, dlen);
181
182	if (option == NULL) {
183		goto done;
184	}
185
186	in = strdup(option);
187
188	while ((a = strsep(&in, ",")) != NULL) {
189		switch (*a) {
190			case 'm':
191				a += strlen("m=");
192				if ((getnum(a, &tmp)) == -1) {
193					--error;
194				} else {
195					memory = tmp;
196				}
197				break;
198			case 't':
199				a += strlen("t=");
200				if ((getnum(a, &tmp)) == -1) {
201					--error;
202				} else {
203					time = tmp;
204				}
205				break;
206			case 'p':
207				a += strlen("p=");
208				if ((getnum(a, &tmp)) == -1) {
209					--error;
210				} else {
211					threads = tmp;
212				}
213				break;
214			default:
215				--error;
216		}
217	}
218
219	free(in);
220
221done:
222	/*
223	 * If parameters are unspecified, calculate some reasonable
224	 * ones based on system time.
225	 */
226	if (memory < ARGON2_MIN_MEMORY ||
227	    time < ARGON2_MIN_TIME ||
228	    threads < ARGON2_MIN_THREADS) {
229		estimate_argon2_params(atype, &time, &memory, &threads);
230	}
231
232	snprintf(dst, dlen, "m=%d,t=%d,p=%d", memory, time, threads);
233
234	return error;
235}
236
237
238static int
239__gensalt_argon2(char *salt, size_t saltsiz,
240    const char *option, argon2_type atype)
241{
242	int rc;
243	int n;
244	char buf[64];
245
246	/* get param, enforcing order and applying defaults */
247	if ((rc = __gensalt_argon2_decode_option(buf,
248	    sizeof(buf), option, atype)) < 0) {
249		return 0;
250	}
251
252	n = snprintf(salt, saltsiz, "$%s$v=%d$%s$",
253		argon2_type2string(atype,0), ARGON2_VERSION_NUMBER, buf);
254
255	if ((size_t)n + 16 >= saltsiz) {
256		return 0;
257	}
258
259	__crypt_tobase64(&salt[n], arc4random(), 4);
260	__crypt_tobase64(&salt[n + 4], arc4random(), 4);
261	__crypt_tobase64(&salt[n + 8], arc4random(), 4);
262	__crypt_tobase64(&salt[n + 12], arc4random(), 4);
263
264	salt[n + 16] = '$';
265	salt[n + 17] = '\0';
266
267	return 0;
268}
269
270/* argon2 variant-specific hooks to generic */
271crypt_private int
272__gensalt_argon2id(char *salt, size_t saltsiz, const char *option)
273{
274	return __gensalt_argon2(salt, saltsiz, option, Argon2_id);
275}
276
277crypt_private int
278__gensalt_argon2i(char *salt, size_t saltsiz, const char *option)
279{
280	return __gensalt_argon2(salt, saltsiz, option, Argon2_i);
281}
282
283crypt_private int
284__gensalt_argon2d(char *salt, size_t saltsiz, const char *option)
285{
286	return __gensalt_argon2(salt, saltsiz, option, Argon2_d);
287}
288
289#endif /* HAVE_ARGON2 */
290
291
292int
293pw_gensalt(char *salt, size_t saltlen, const char *type, const char *option)
294{
295	const struct pw_salt *sp;
296
297	for (sp = salts; sp->name; sp++)
298		if (strcmp(sp->name, type) == 0)
299			return (*sp->gensalt)(salt, saltlen, option);
300
301	errno = EINVAL;
302	return -1;
303}
304