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