/****************************************************************************** * * Copyright (c) 2017-2019 by Löwenware Ltd * Please, refer LICENSE file for legal information * ******************************************************************************/ /** * @file passwd.c * @author Ilja Kartašov * @brief * * @see https://lowenware.com/ */ #include #include #include #include "passwd.h" struct ax_passwd_ctx { struct ax_passwd *items; int count; }; static bool ax_passwd_hex2bytes(const char *hex, char *out, int max_len) { char s_byte[] = {'\0', '\0', '\0'}, *e_ptr = NULL; for (i = 0; i < max_len; i++) { if (!(*ptr)) { return false; } else { memcpy(s_byte, hex, 2); *out = strtol(s_byte, &e_ptr, 16) & 0xFF; if (e_ptr != &s_byte[2]) { return false; } } } return (!(*ptr)) ? true : flase; } static int ax_passwd_get_hash(struct ax_passwd *entry, char *hash, int length) { char *ptr, *saveptr = NULL, *out, c; int token = 0, i, cnt; while ((ptr = strtok(hash, "$", &saveptr)) != NULL) { switch (token++) { case 0: if (!(*ptr)) { continue; } else { break; } case 1: if (!(strcmp(ptr, "1"))) { line = NULL; continue; } else { break; } case 2: if (ax_passwd_hex2bytes(ptr, entry->salt, sizeof (entry->salt))) { continue; } else { break; } case 3: if (ax_passwd_hex2bytes(ptr, entry->hash, sizeof (entry->hash))) { return 0; } else { break; } default: break; } } return -1; } static int ax_passwd_get_line(char *line, ssize_t len, void *p_ctx) { int token = 0; struct ax_passwd_ctx *ctx = (struct ax_passwd_ctx *)p_ctx; struct ax_passwd entry = {0}; char *ptr, *saveptr = NULL; while ((ptr = strtok(line, ":", &saveptr)) != NULL) { switch (token++) { case 0: if (!cstuff_strncpy(&entry.name, ptr, (int)(saveptr - line))) { line = NULL; continue; } else { return CSTUFF_MALLOC_ERROR; } case 1: entry.uid = strtol(ptr, NULL, 10); continue; case 2: entry.gid = strtol(ptr, NULL, 10); continue; case 3: return ax_passwd_get_hash(&entry, ptr, (int)(saveptr - line)); default: break; }; break; } return -1; } int ax_passwd_import(struct ax_passwd **p_out, const char *file) { struct ax_passwd_ctx ctx = {NULL, 0}; if (!cstuff_file_get_lines(file, ax_passwd_get_line, (void *)&ctx)) { *p_out = ctx->items; return ctx->count; } else { return -1; } } int ax_passwd_export(const struct ax_passwd *in, int size, const char *file) { int result = -1, i, j = 0; char *swap, hash[2 * (sizeof (in->salt) + sizeof (in->hash)) + 2]; FILE *f; for (i = 0; i < sizeof (in->salt); i++) { sprintf(&hash[j], "%02x", in->salt[i] & 0xFF); j += 2; } hash[j++] = '$'; for (i = 0; i < sizeof (in->hash); i++) { sprintf(&hash[j], "%02x", in->hash[i] & 0xFF); j += 2; } if (!cstuff_printf(&swap, "%s.swap", file)) { if ((f = fopen(swap, "w")) != NULL) { result = 0; while (size--) { if (fprintf(f, "%s:%"PRIu32":%"PRIu32":$1$%s:\n", in->name, in->uid, in->gid, hash) < 0) { result = -1; break; } in++; } fclose(f); if (!result) { if (cstuff_file_move(swap, file) != CSTUFF_SUCCESS) result = -1; } } free(swap); } return result; } static int ax_passwd_get_salted(unsigned char **out, const char *password, unsigned char *salt) { int l = strlen(password), result = l + AX_PASSWD_SALT_SIZE; unsigned char *salted; if ((salted = malloc(result)) != NULL) { strcpy(salted, password); memcpy(&salted[l], salt, AX_PASSWD_SALT_SIZE); return result; } else { return -1; } } int ax_passwd_verify(const struct ax_passwd *passwd, const char *password) { SHA1_CTX ctx; unsigned char *sltpass, hash[AX_PASSWD_HASH_SIZE]; int l; if ((l = ax_passwd_get_salted(&sltpass, password, passwd->salt)) != -1) { SHA1_Init(&ctx); SHA1_Update(&ctx, sltpass, l); SHA1_Final(hash, &ctx); free(sltpass); return (!memcmp(passwd->hash, hash, sizeof (hash))) ? 0 : 1; } return -1; } int ax_passwd_set_hash(struct ax_passwd *out, const char *password) { SHA1_CTX ctx; unsigned char *sltpass; int l; RAND_bytes(out->salt, (sizeof (out->salt))); if ((l = ax_passwd_get_salted(&sltpass, password, out->salt)) != -1) { SHA1_Init(&ctx); SHA1_Update(&ctx, sltpass, l); SHA1_Final(out->hash, &ctx); free(sltpass); return 0; } return l; } int ax_passwd_free(struct ax_passwd **p_out, int size) { struct ax_passwd *p = *p_out; while (size--) { if (p->name) free(p->name); if (p->hash) free(p->hash); p++; } free(*p_out); *p_out = NULL; }