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*/ 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*/ 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*/ 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 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 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 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 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 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 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 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