1 /*        $NetBSD: pw_gensalt.c,v 1.14 2024/07/23 22:37:11 riastradh 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.14 2024/07/23 22:37:11 riastradh 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 
63 crypt_private int
64 estimate_argon2_params(argon2_type, uint32_t *, uint32_t *, uint32_t *);
65 #endif /* HAVE_ARGON2 */
66 
67 static 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 
87 crypt_private int
88 /*ARGSUSED2*/
__gensalt_old(char * salt,size_t saltsiz,const char * option)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 
100 crypt_private int
101 /*ARGSUSED2*/
__gensalt_new(char * salt,size_t saltsiz,const char * option)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 
126 crypt_private int
127 /*ARGSUSED2*/
__gensalt_md5(char * salt,size_t saltsiz,const char * option)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 
144 crypt_private int
__gensalt_sha1(char * salt,size_t saltsiz,const char * option)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
168 static int
__gensalt_argon2_decode_option(char * dst,size_t dlen,const char * option,argon2_type atype)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 
221 done:
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 
238 static int
__gensalt_argon2(char * salt,size_t saltsiz,const char * option,argon2_type atype)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 */
271 crypt_private int
__gensalt_argon2id(char * salt,size_t saltsiz,const char * option)272 __gensalt_argon2id(char *salt, size_t saltsiz, const char *option)
273 {
274           return __gensalt_argon2(salt, saltsiz, option, Argon2_id);
275 }
276 
277 crypt_private int
__gensalt_argon2i(char * salt,size_t saltsiz,const char * option)278 __gensalt_argon2i(char *salt, size_t saltsiz, const char *option)
279 {
280           return __gensalt_argon2(salt, saltsiz, option, Argon2_i);
281 }
282 
283 crypt_private int
__gensalt_argon2d(char * salt,size_t saltsiz,const char * option)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 
292 int
pw_gensalt(char * salt,size_t saltlen,const char * type,const char * option)293 pw_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