mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Add standalone host PBKDF2-HMAC-SHA256 mkpasswd, board_romfs_mkpasswd.sh, and promptpasswd.sh with confirm-password support. Integrate ROMFS passwd generation in Board.mk and CMake. Drop TEA key checks from passwd_keys.mk. Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
781 lines
19 KiB
C
781 lines
19 KiB
C
/****************************************************************************
|
|
* tools/mkpasswd.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Description:
|
|
* Host build tool that generates a NuttX /etc/passwd entry with a
|
|
* PBKDF2-HMAC-SHA256 password hash. This is a pure C replacement for the
|
|
* former tools/mkpasswd.py, removing the Python dependency from the build.
|
|
*
|
|
* The hash format is identical to that used at runtime by:
|
|
* apps/fsutils/passwd/passwd_encrypt.c
|
|
* apps/fsutils/passwd/passwd_verify.c
|
|
*
|
|
* Usage:
|
|
* mkpasswd --user <name> --password <pass> [options] [-o <output>]
|
|
*
|
|
* Options:
|
|
* --user <str> Username (required)
|
|
* --password <str> Plaintext password (required, not stored in output)
|
|
* --uid <int> User ID (default: 0)
|
|
* --gid <int> Group ID (default: 0)
|
|
* --home <str> Home directory (default: /)
|
|
* --iterations <int> PBKDF2 iterations (default: 10000)
|
|
* -o <path> Output file (default: stdout)
|
|
*
|
|
* Output format (matches NuttX passwd file format):
|
|
* username:$pbkdf2-sha256$<iter>$<salt>$<hash>:uid:gid:home
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#ifndef _POSIX_C_SOURCE
|
|
# define _POSIX_C_SOURCE 200809L
|
|
#endif
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#ifndef CONFIG_WINDOWS_NATIVE
|
|
# include <fcntl.h>
|
|
# include <sys/random.h>
|
|
# include <sys/stat.h>
|
|
# include <unistd.h>
|
|
#else
|
|
# include <direct.h>
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define MKPASSWD_NL "\n\n"
|
|
#define PASSWD_MCF_PREFIX "$pbkdf2-sha256$"
|
|
#define PASSWD_SALT_BYTES 16
|
|
#define PASSWD_HASH_BYTES 32
|
|
#define MAX_ENCRYPTED 96
|
|
#define MAX_PASSWORD 256
|
|
#define MIN_PASSWORD 8
|
|
#define DEFAULT_ITERATIONS 10000
|
|
#define MIN_ITERATIONS 1000
|
|
#define MAX_ITERATIONS 200000
|
|
|
|
static const char g_base64url[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
|
|
/****************************************************************************
|
|
* Private Types (minimal SHA-256)
|
|
****************************************************************************/
|
|
|
|
struct sha256_ctx
|
|
{
|
|
uint32_t state[8];
|
|
uint64_t bitlen;
|
|
uint8_t data[64];
|
|
uint32_t datalen;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions (SHA-256 + HMAC-SHA256 + PBKDF2)
|
|
****************************************************************************/
|
|
|
|
static uint32_t rotr32(uint32_t x, uint32_t n)
|
|
{
|
|
return (x >> n) | (x << (32 - n));
|
|
}
|
|
|
|
static void sha256_transform(struct sha256_ctx *ctx,
|
|
const uint8_t data[64])
|
|
{
|
|
static const uint32_t k[64] =
|
|
{
|
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
|
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
|
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
|
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
};
|
|
|
|
uint32_t m[64];
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
uint32_t e;
|
|
uint32_t f;
|
|
uint32_t g;
|
|
uint32_t h;
|
|
uint32_t t1;
|
|
uint32_t t2;
|
|
uint32_t s0;
|
|
uint32_t s1;
|
|
int i;
|
|
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
m[i] = ((uint32_t)data[i * 4] << 24) |
|
|
((uint32_t)data[i * 4 + 1] << 16) |
|
|
((uint32_t)data[i * 4 + 2] << 8) |
|
|
((uint32_t)data[i * 4 + 3]);
|
|
}
|
|
|
|
for (i = 16; i < 64; i++)
|
|
{
|
|
s0 = rotr32(m[i - 15], 7) ^ rotr32(m[i - 15], 18) ^
|
|
(m[i - 15] >> 3);
|
|
s1 = rotr32(m[i - 2], 17) ^ rotr32(m[i - 2], 19) ^
|
|
(m[i - 2] >> 10);
|
|
|
|
m[i] = m[i - 16] + s0 + m[i - 7] + s1;
|
|
}
|
|
|
|
a = ctx->state[0];
|
|
b = ctx->state[1];
|
|
c = ctx->state[2];
|
|
d = ctx->state[3];
|
|
e = ctx->state[4];
|
|
f = ctx->state[5];
|
|
g = ctx->state[6];
|
|
h = ctx->state[7];
|
|
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
t1 = h + (rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25)) +
|
|
((e & f) ^ ((~e) & g)) + k[i] + m[i];
|
|
t2 = (rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22)) +
|
|
((a & b) ^ (a & c) ^ (b & c));
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t1;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t1 + t2;
|
|
}
|
|
|
|
ctx->state[0] += a;
|
|
ctx->state[1] += b;
|
|
ctx->state[2] += c;
|
|
ctx->state[3] += d;
|
|
ctx->state[4] += e;
|
|
ctx->state[5] += f;
|
|
ctx->state[6] += g;
|
|
ctx->state[7] += h;
|
|
}
|
|
|
|
static void sha256_init(struct sha256_ctx *ctx)
|
|
{
|
|
ctx->datalen = 0;
|
|
ctx->bitlen = 0;
|
|
ctx->state[0] = 0x6a09e667;
|
|
ctx->state[1] = 0xbb67ae85;
|
|
ctx->state[2] = 0x3c6ef372;
|
|
ctx->state[3] = 0xa54ff53a;
|
|
ctx->state[4] = 0x510e527f;
|
|
ctx->state[5] = 0x9b05688c;
|
|
ctx->state[6] = 0x1f83d9ab;
|
|
ctx->state[7] = 0x5be0cd19;
|
|
}
|
|
|
|
static void sha256_update(struct sha256_ctx *ctx,
|
|
const uint8_t *data, size_t len)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
ctx->data[ctx->datalen] = data[i];
|
|
ctx->datalen++;
|
|
if (ctx->datalen == 64)
|
|
{
|
|
sha256_transform(ctx, ctx->data);
|
|
ctx->bitlen += 512;
|
|
ctx->datalen = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sha256_final(struct sha256_ctx *ctx, uint8_t hash[32])
|
|
{
|
|
uint32_t i;
|
|
uint32_t j;
|
|
|
|
i = ctx->datalen;
|
|
|
|
if (ctx->datalen < 56)
|
|
{
|
|
ctx->data[i++] = 0x80;
|
|
while (i < 56)
|
|
{
|
|
ctx->data[i++] = 0x00;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ctx->data[i++] = 0x80;
|
|
while (i < 64)
|
|
{
|
|
ctx->data[i++] = 0x00;
|
|
}
|
|
|
|
sha256_transform(ctx, ctx->data);
|
|
memset(ctx->data, 0, 56);
|
|
}
|
|
|
|
ctx->bitlen += (uint64_t)ctx->datalen * 8;
|
|
ctx->data[63] = (uint8_t)(ctx->bitlen);
|
|
ctx->data[62] = (uint8_t)(ctx->bitlen >> 8);
|
|
ctx->data[61] = (uint8_t)(ctx->bitlen >> 16);
|
|
ctx->data[60] = (uint8_t)(ctx->bitlen >> 24);
|
|
ctx->data[59] = (uint8_t)(ctx->bitlen >> 32);
|
|
ctx->data[58] = (uint8_t)(ctx->bitlen >> 40);
|
|
ctx->data[57] = (uint8_t)(ctx->bitlen >> 48);
|
|
ctx->data[56] = (uint8_t)(ctx->bitlen >> 56);
|
|
sha256_transform(ctx, ctx->data);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
for (j = 0; j < 8; j++)
|
|
{
|
|
hash[i + (j * 4)] = (uint8_t)((ctx->state[j] >>
|
|
(24 - i * 8)) & 0xff);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hmac_sha256(const uint8_t *key, size_t keylen,
|
|
const uint8_t *data, size_t datalen,
|
|
uint8_t mac[32])
|
|
{
|
|
struct sha256_ctx ctx;
|
|
uint8_t k_ipad[64];
|
|
uint8_t k_opad[64];
|
|
uint8_t tk[32];
|
|
size_t i;
|
|
|
|
if (keylen > 64)
|
|
{
|
|
sha256_init(&ctx);
|
|
sha256_update(&ctx, key, keylen);
|
|
sha256_final(&ctx, tk);
|
|
key = tk;
|
|
keylen = 32;
|
|
}
|
|
|
|
memset(k_ipad, 0, sizeof(k_ipad));
|
|
memset(k_opad, 0, sizeof(k_opad));
|
|
memcpy(k_ipad, key, keylen);
|
|
memcpy(k_opad, key, keylen);
|
|
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
k_ipad[i] ^= 0x36;
|
|
k_opad[i] ^= 0x5c;
|
|
}
|
|
|
|
sha256_init(&ctx);
|
|
sha256_update(&ctx, k_ipad, 64);
|
|
sha256_update(&ctx, data, datalen);
|
|
sha256_final(&ctx, mac);
|
|
|
|
sha256_init(&ctx);
|
|
sha256_update(&ctx, k_opad, 64);
|
|
sha256_update(&ctx, mac, 32);
|
|
sha256_final(&ctx, mac);
|
|
}
|
|
|
|
static int pbkdf2_hmac_sha256(const uint8_t *pass, size_t passlen,
|
|
const uint8_t *salt, size_t saltlen,
|
|
uint32_t iterations,
|
|
uint8_t *out, size_t outlen)
|
|
{
|
|
uint8_t u[32];
|
|
uint8_t t[32];
|
|
uint8_t saltblk[64];
|
|
size_t generated = 0;
|
|
uint32_t block;
|
|
uint32_t i;
|
|
uint32_t j;
|
|
|
|
if (iterations == 0 || outlen == 0 || saltlen + 4 > sizeof(saltblk))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
for (block = 1; generated < outlen; block++)
|
|
{
|
|
memcpy(saltblk, salt, saltlen);
|
|
saltblk[saltlen + 0] = (uint8_t)((block >> 24) & 0xff);
|
|
saltblk[saltlen + 1] = (uint8_t)((block >> 16) & 0xff);
|
|
saltblk[saltlen + 2] = (uint8_t)((block >> 8) & 0xff);
|
|
saltblk[saltlen + 3] = (uint8_t)(block & 0xff);
|
|
|
|
hmac_sha256(pass, passlen, saltblk, saltlen + 4, u);
|
|
memcpy(t, u, sizeof(t));
|
|
|
|
for (i = 1; i < iterations; i++)
|
|
{
|
|
hmac_sha256(pass, passlen, u, sizeof(u), u);
|
|
for (j = 0; j < 32; j++)
|
|
{
|
|
t[j] ^= u[j];
|
|
}
|
|
}
|
|
|
|
if (outlen - generated >= 32)
|
|
{
|
|
memcpy(out + generated, t, 32);
|
|
generated += 32;
|
|
}
|
|
else
|
|
{
|
|
memcpy(out + generated, t, outlen - generated);
|
|
generated = outlen;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fill_random(uint8_t *buf, size_t len)
|
|
{
|
|
#ifndef CONFIG_WINDOWS_NATIVE
|
|
ssize_t nread;
|
|
int fd;
|
|
|
|
# ifdef SYS_getrandom
|
|
nread = getrandom(buf, len, 0);
|
|
if (nread == (ssize_t)len)
|
|
{
|
|
return 0;
|
|
}
|
|
# endif
|
|
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
nread = read(fd, buf, len);
|
|
close(fd);
|
|
|
|
return nread == (ssize_t)len ? 0 : -1;
|
|
#else
|
|
(void)buf;
|
|
(void)len;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
static int base64url_encode(const uint8_t *in, size_t inlen,
|
|
char *out, size_t outlen)
|
|
{
|
|
uint32_t acc = 0;
|
|
size_t i;
|
|
size_t o = 0;
|
|
int bits = 0;
|
|
|
|
for (i = 0; i < inlen; i++)
|
|
{
|
|
acc = (acc << 8) | in[i];
|
|
bits += 8;
|
|
|
|
while (bits >= 6)
|
|
{
|
|
if (o + 1 >= outlen)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
bits -= 6;
|
|
out[o++] = g_base64url[(acc >> bits) & 0x3f];
|
|
}
|
|
}
|
|
|
|
if (bits > 0)
|
|
{
|
|
if (o + 1 >= outlen)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
out[o++] = g_base64url[(acc << (6 - bits)) & 0x3f];
|
|
}
|
|
|
|
if (o >= outlen)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
out[o] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static int validate_password_complexity(const char *password)
|
|
{
|
|
const char *specials = "!@#$%^&*()_+-=[]{}|;:,.<>?";
|
|
const char *p;
|
|
int has_upper = 0;
|
|
int has_lower = 0;
|
|
int has_digit = 0;
|
|
int has_special = 0;
|
|
|
|
if (strlen(password) < MIN_PASSWORD)
|
|
{
|
|
fprintf(stderr, "\nError: password must be at least 8 characters\n\n");
|
|
return -1;
|
|
}
|
|
|
|
if (strlen(password) > MAX_PASSWORD)
|
|
{
|
|
fprintf(stderr, "\nError: password must be at most %d characters\n\n",
|
|
MAX_PASSWORD);
|
|
return -1;
|
|
}
|
|
|
|
for (p = password; *p; p++)
|
|
{
|
|
if (isupper((unsigned char)*p))
|
|
{
|
|
has_upper = 1;
|
|
}
|
|
else if (islower((unsigned char)*p))
|
|
{
|
|
has_lower = 1;
|
|
}
|
|
else if (isdigit((unsigned char)*p))
|
|
{
|
|
has_digit = 1;
|
|
}
|
|
else if (strchr(specials, *p))
|
|
{
|
|
has_special = 1;
|
|
}
|
|
}
|
|
|
|
if (!has_upper)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: password must contain at least one uppercase "
|
|
"letter (A-Z)\n\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!has_lower)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: password must contain at least one lowercase "
|
|
"letter (a-z)\n\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!has_digit)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: password must contain at least one digit "
|
|
"(0-9)\n\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!has_special)
|
|
{
|
|
fprintf(stderr,
|
|
"\nError: password must contain at least one special "
|
|
"character (!@#$%%^&*()_+-=[]{}|;:,.<>?)\n\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int passwd_hash(const char *password,
|
|
uint32_t iterations,
|
|
char encrypted[MAX_ENCRYPTED + 1])
|
|
{
|
|
uint8_t salt[PASSWD_SALT_BYTES];
|
|
uint8_t hash[PASSWD_HASH_BYTES];
|
|
char salt_b64[32];
|
|
char hash_b64[48];
|
|
size_t passlen;
|
|
int ret;
|
|
|
|
passlen = strlen(password);
|
|
|
|
if (fill_random(salt, sizeof(salt)) < 0)
|
|
{
|
|
fputs(MKPASSWD_NL, stderr);
|
|
fprintf(stderr, "mkpasswd: cannot obtain random salt\n");
|
|
return -1;
|
|
}
|
|
|
|
if (pbkdf2_hmac_sha256((const uint8_t *)password, passlen,
|
|
salt, sizeof(salt), iterations,
|
|
hash, sizeof(hash)) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (base64url_encode(salt, sizeof(salt), salt_b64,
|
|
sizeof(salt_b64)) < 0 ||
|
|
base64url_encode(hash, sizeof(hash), hash_b64,
|
|
sizeof(hash_b64)) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ret = snprintf(encrypted, MAX_ENCRYPTED + 1,
|
|
PASSWD_MCF_PREFIX "%u$%s$%s",
|
|
iterations, salt_b64, hash_b64);
|
|
if (ret < 0 || (size_t)ret > MAX_ENCRYPTED)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mkdir_p(const char *path)
|
|
{
|
|
char *tmp;
|
|
char *p;
|
|
size_t len;
|
|
|
|
tmp = strdup(path);
|
|
if (tmp == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
len = strlen(tmp);
|
|
|
|
if (len > 0 && tmp[len - 1] == '/')
|
|
{
|
|
tmp[len - 1] = '\0';
|
|
}
|
|
|
|
for (p = tmp + 1; *p != '\0'; p++)
|
|
{
|
|
if (*p == '/')
|
|
{
|
|
*p = '\0';
|
|
#ifndef CONFIG_WINDOWS_NATIVE
|
|
mkdir(tmp, 0755);
|
|
#else
|
|
mkdir(tmp);
|
|
#endif
|
|
*p = '/';
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_WINDOWS_NATIVE
|
|
mkdir(tmp, 0755);
|
|
#else
|
|
mkdir(tmp);
|
|
#endif
|
|
free(tmp);
|
|
return 0;
|
|
}
|
|
|
|
static void show_usage(const char *progname)
|
|
{
|
|
fprintf(stderr,
|
|
"Usage: %s --user <name> --password <pass> [options] [-o <file>]\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" --user <str> Username (required)\n"
|
|
" --password <str> Plaintext password (required)\n"
|
|
" --uid <int> User ID (default: 0)\n"
|
|
" --gid <int> Group ID (default: 0)\n"
|
|
" --home <str> Home directory (default: /)\n"
|
|
" --iterations <int> PBKDF2 iterations (default: %d)\n"
|
|
" -o <path> Output file (default: stdout)\n"
|
|
"\n"
|
|
"Output format: username:$pbkdf2-sha256$...:uid:gid:home\n",
|
|
progname, DEFAULT_ITERATIONS);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
const char *user;
|
|
const char *password;
|
|
const char *home;
|
|
const char *outpath;
|
|
FILE *out;
|
|
char encrypted[MAX_ENCRYPTED + 1];
|
|
int uid;
|
|
int gid;
|
|
uint32_t iterations;
|
|
int i;
|
|
int ret;
|
|
|
|
user = NULL;
|
|
password = NULL;
|
|
home = "/";
|
|
outpath = NULL;
|
|
uid = 0;
|
|
gid = 0;
|
|
iterations = DEFAULT_ITERATIONS;
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "--user") == 0 && i + 1 < argc)
|
|
{
|
|
user = argv[++i];
|
|
}
|
|
else if (strcmp(argv[i], "--password") == 0 && i + 1 < argc)
|
|
{
|
|
password = argv[++i];
|
|
}
|
|
else if (strcmp(argv[i], "--uid") == 0 && i + 1 < argc)
|
|
{
|
|
uid = atoi(argv[++i]);
|
|
}
|
|
else if (strcmp(argv[i], "--gid") == 0 && i + 1 < argc)
|
|
{
|
|
gid = atoi(argv[++i]);
|
|
}
|
|
else if (strcmp(argv[i], "--home") == 0 && i + 1 < argc)
|
|
{
|
|
home = argv[++i];
|
|
}
|
|
else if (strcmp(argv[i], "--iterations") == 0 && i + 1 < argc)
|
|
{
|
|
iterations = (uint32_t)strtoul(argv[++i], NULL, 10);
|
|
}
|
|
else if ((strcmp(argv[i], "-o") == 0 ||
|
|
strcmp(argv[i], "--output") == 0) && i + 1 < argc)
|
|
{
|
|
outpath = argv[++i];
|
|
}
|
|
else if (strcmp(argv[i], "--help") == 0 ||
|
|
strcmp(argv[i], "-h") == 0)
|
|
{
|
|
show_usage(argv[0]);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
fputs(MKPASSWD_NL, stderr);
|
|
fprintf(stderr, "mkpasswd: unknown option: %s\n", argv[i]);
|
|
show_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (user == NULL)
|
|
{
|
|
fputs(MKPASSWD_NL, stderr);
|
|
fprintf(stderr, "mkpasswd: --user is required\n");
|
|
show_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (password == NULL)
|
|
{
|
|
fputs(MKPASSWD_NL, stderr);
|
|
fprintf(stderr, "mkpasswd: --password is required\n");
|
|
show_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (validate_password_complexity(password) < 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (iterations < MIN_ITERATIONS || iterations > MAX_ITERATIONS)
|
|
{
|
|
fputs(MKPASSWD_NL, stderr);
|
|
fprintf(stderr,
|
|
"mkpasswd: --iterations must be between %d and %d\n",
|
|
MIN_ITERATIONS, MAX_ITERATIONS);
|
|
return 1;
|
|
}
|
|
|
|
ret = passwd_hash(password, iterations, encrypted);
|
|
if (ret < 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (outpath != NULL)
|
|
{
|
|
char *dir;
|
|
char *last;
|
|
|
|
dir = strdup(outpath);
|
|
last = strrchr(dir, '/');
|
|
|
|
if (last != NULL && last != dir)
|
|
{
|
|
*last = '\0';
|
|
mkdir_p(dir);
|
|
}
|
|
|
|
free(dir);
|
|
|
|
out = fopen(outpath, "w");
|
|
if (out == NULL)
|
|
{
|
|
fputs(MKPASSWD_NL, stderr);
|
|
fprintf(stderr, "mkpasswd: cannot open output file '%s': %s\n",
|
|
outpath, strerror(errno));
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out = stdout;
|
|
}
|
|
|
|
fprintf(out, "%s:%s:%d:%d:%s\n", user, encrypted, uid, gid, home);
|
|
|
|
if (outpath != NULL)
|
|
{
|
|
fclose(out);
|
|
}
|
|
|
|
return 0;
|
|
}
|