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