From patchwork Wed Nov 28 04:20:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Mendoza-Jonas X-Patchwork-Id: 1004181 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 434SCQ45jlz9s1c for ; Wed, 28 Nov 2018 15:21:38 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=mendozajonas.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="pT6PYEYC"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="Bo8Lj19J"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 434SCQ2Dr1zDqgC for ; Wed, 28 Nov 2018 15:21:38 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=mendozajonas.com Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="pT6PYEYC"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="Bo8Lj19J"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=mendozajonas.com (client-ip=66.111.4.28; helo=out4-smtp.messagingengine.com; envelope-from=sam@mendozajonas.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=mendozajonas.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="pT6PYEYC"; dkim=pass (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="Bo8Lj19J"; dkim-atps=neutral Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 434SBJ2vgPzDqg7 for ; Wed, 28 Nov 2018 15:20:40 +1100 (AEDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id EDCD32201E; Tue, 27 Nov 2018 23:20:32 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Tue, 27 Nov 2018 23:20:32 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= mendozajonas.com; h=from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding; s=fm1; bh=3Byqos525mbO5TOmsVI3ICTBk6FfyHHWVf6bi52Gc+I=; b=pT6PY EYC2+UJ30K6POR+KxWy4jgsLZOcWF4qMdKJ2x4jffojxuWwwHgomQs++QkhLQWGG uYYLGl1GmNzx5ddRmuj5QcMPjWiCtBAeC5sbrzrJ+I0vTjp93HqUrQ5moMIGwmk0 R36GATQO8Of85Sj6AnxVlJjWd6zTM7us7vIwEeBSUX2g2/YwqgoOV8uDeS6WFW7F mtU7h0y5bxc4SgAnO+lsZMHrr4JV9RK56eI0tPgJCoyphhvunSGARD1B6skmRuou Ws2UcY2Z/JnxQoDuERauOZ4Ka1FW+RlsqLMgQ0/5M7gzF5q27YX5ITEgv2r/Rudi unHmYX+o2TmBbiZdg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=3Byqos525mbO5TOmsVI3ICTBk6FfyHHWVf6bi52Gc+I=; b=Bo8Lj19J G/NaCgGdxW9HERKJfIUm7gz9CKUtGqVD5q8phhK8rohtqCQCgU99mkqfWAy9ESEe J/5rGeFqT8dR0kA25HXCvp1q8Fw70cf39lvIWplYW+TiQiqpmXWJ9RiJYowA3BW0 7MdK6mexrmGMa0Ps86fuDLi4WA+5yaA+X9vzy/mD0fYuBsucX9VI+Q80Hnhux1v4 CJ/1JQ9oNpZ5vVgTpxoUipUuHZDNM2r2y24OV2HnHFgvXPqhPqi57RYJaWb/bd+c 0yANGhXS0XHEsDpefILnkDsP08Cuzbqkp30SI3fIxhmfyqIuWdoK00eybvqoRIqU T9NEKfgz37oPrQ== X-ME-Sender: X-ME-Proxy: Received: from v4.ozlabs.ibm.com (unknown [122.99.82.10]) by mail.messagingengine.com (Postfix) with ESMTPA id 48668102F2; Tue, 27 Nov 2018 23:20:31 -0500 (EST) From: Samuel Mendoza-Jonas To: petitboot@lists.ozlabs.org Subject: [PATCH v2 05/13] lib/crypt: Add helpers for operating on /etc/shadow Date: Wed, 28 Nov 2018 15:20:04 +1100 Message-Id: <20181128042012.25916-6-sam@mendozajonas.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181128042012.25916-1-sam@mendozajonas.com> References: <20181128042012.25916-1-sam@mendozajonas.com> MIME-Version: 1.0 X-BeenThere: petitboot@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Petitboot bootloader development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Samuel Mendoza-Jonas Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" Provides helper functions for reading, writing, and checking against /etc/shadow. The main use case if for authenticating clients against the "system" password, which is set as the root password. Signed-off-by: Samuel Mendoza-Jonas --- configure.ac | 22 +++++ lib/Makefile.am | 9 ++ lib/crypt/crypt.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++ lib/crypt/crypt.h | 49 +++++++++++ 4 files changed, 297 insertions(+) create mode 100644 lib/crypt/crypt.c create mode 100644 lib/crypt/crypt.h diff --git a/configure.ac b/configure.ac index 2bf6e6f6..4151b002 100644 --- a/configure.ac +++ b/configure.ac @@ -76,6 +76,27 @@ AC_CHECK_LIB([devmapper], [dm_task_create], [AC_MSG_FAILURE([The libdevmapper development library is required by petitboot. Try installing the package libdevmapper-dev or device-mapper-devel.])] ) +AC_ARG_ENABLE( + [crypt], + [AS_HELP_STRING( + [--enable-crypt], + [Include crypt support to enable password use [default=no]] + )], + [], + [enable_crypt=no] +) +AM_CONDITIONAL([ENABLE_CRYPT], [test "x$enable_crypt" = "xyes"]) +AS_IF([test "x$enable_crypt" = "xyes"], + [AC_DEFINE(CRYPT_SUPPORT, 1, [Enable crypt/password support])], + [] +) +AS_IF([test "x$enable_crypt" = "xyes"], + AC_CHECK_LIB([crypt], [crypt], + [CRYPT_LIBS=-lcrypt], + [AC_MSG_FAILURE([shadow/crypt libs required])] + ) +) + AC_ARG_WITH([fdt], AS_HELP_STRING([--without-fdt], [Build without libfdt (default: no)])) @@ -455,6 +476,7 @@ AS_IF( AC_SUBST([UDEV_LIBS]) AC_SUBST([DEVMAPPER_LIBS]) +AC_SUBST([CRYPT_LIBS]) AC_SUBST([FDT_LIBS]) AC_SUBST([LIBFLASH_LIBS]) AC_SUBST([LIBTOOL_DEPS]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 016a14dd..69a66c37 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -35,6 +35,7 @@ lib_libpbcore_la_CFLAGS = \ lib_libpbcore_la_SOURCES = \ lib/ccan/endian/endian.h \ + lib/crypt/crypt.h \ lib/file/file.h \ lib/file/file.c \ lib/fold/fold.h \ @@ -93,3 +94,11 @@ lib_libpbcore_la_SOURCES += \ lib/security/none.c endif endif + +if ENABLE_CRYPT +lib_libpbcore_la_SOURCES += \ + lib/crypt/crypt.c + +lib_libpbcore_la_LDFLAGS += \ + $(CRYPT_LIBS) +endif diff --git a/lib/crypt/crypt.c b/lib/crypt/crypt.c new file mode 100644 index 00000000..b5e183a9 --- /dev/null +++ b/lib/crypt/crypt.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2018 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "crypt.h" + +int crypt_set_password_hash(void *ctx, const char *hash) +{ + struct spwd **shadow, *entry; + bool found_root; + int rc, i, n; + FILE *fp; + + if (lckpwdf()) { + pb_log("Could not obtain access to shadow file\n"); + return -1; + } + setspent(); + + found_root = false; + shadow = NULL; + n = 0; + + /* Read all entries and modify the root entry */ + errno = 0; + fp = fopen("/etc/shadow", "r"); + if (!fp) { + pb_log("Could not open shadow file, %m\n"); + rc = -1; + goto out; + } + + entry = fgetspent(fp); + while (entry) { + shadow = talloc_realloc(ctx, shadow, struct spwd *, n + 1); + if (!shadow) { + pb_log("Failed to allocate shadow struct\n"); + rc = -1; + goto out; + } + + shadow[n] = talloc_memdup(shadow, entry, sizeof(struct spwd)); + if (!shadow[n]) { + pb_log("Could not duplicate entry for %s\n", + entry->sp_namp); + rc = -1; + goto out; + } + + shadow[n]->sp_namp = talloc_strdup(shadow, entry->sp_namp); + if (strncmp(shadow[n]->sp_namp, "root", strlen("root")) == 0) { + shadow[n]->sp_pwdp = talloc_strdup(shadow, hash); + found_root = true; + } else { + shadow[n]->sp_pwdp = talloc_strdup(shadow, + entry->sp_pwdp); + } + + if (!shadow[n]->sp_namp || !shadow[n]->sp_pwdp) { + pb_log("Failed to allocate new fields for %s\n", + entry->sp_namp); + rc = -1; + goto out; + } + + n++; + entry = fgetspent(fp); + } + + if (n == 0) + pb_debug_fn("No entries found\n"); + + fclose(fp); + + if (!found_root) { + /* Make our own */ + pb_debug_fn("No root user found, creating entry\n"); + shadow = talloc_realloc(ctx, shadow, struct spwd *, n + 1); + if (!shadow) { + pb_log("Failed to allocate shadow struct\n"); + rc = -1; + goto out; + } + + shadow[n] = talloc_zero(shadow, struct spwd); + shadow[n]->sp_namp = talloc_asprintf(shadow, "root"); + shadow[n]->sp_pwdp = talloc_strdup(shadow, hash); + if (!shadow[n]->sp_namp || !shadow[n]->sp_pwdp) { + pb_log("Failed to allocate new fields for root entry\n"); + rc = -1; + goto out; + } + n++; + } + + errno = 0; + fp = fopen("/etc/shadow", "w"); + if (!fp) { + pb_log("Could not open shadow file, %m\n"); + rc = -1; + goto out; + } + + /* Write each entry back to keep the same format in /etc/shadow */ + for (i = 0; i < n; i++) { + rc = putspent(shadow[i], fp); + if (rc) + pb_log("Failed to write back shadow entry for %s!\n", + shadow[i]->sp_namp); + } + + rc = 0; +out: + if (fp) + fclose(fp); + talloc_free(shadow); + endspent(); + ulckpwdf(); + return rc; +} + +static const char *crypt_hash_password(const char *password) +{ + struct spwd *shadow; + char *hash, *salt; + char new_salt[17]; + int i; + + shadow = getspnam("root"); + if (!shadow) { + pb_log("Could not find root shadow\n"); + return NULL; + } + + if (shadow->sp_pwdp && strlen(shadow->sp_pwdp)) { + salt = shadow->sp_pwdp; + } else { + for (i = 0; i < 16; i++) + new_salt[i] = random() % 94 + 32; + new_salt[i] = '\0'; + salt = talloc_asprintf(password, "$6$%s", new_salt); + } + + hash = crypt(password ?: "", salt); + if (!hash) + pb_log("Could not create hash, %m\n"); + + + return hash; +} + + +int crypt_set_password(void *ctx, const char *password) +{ + const char *hash; + + if (!password || !strlen(password)) + return crypt_set_password_hash(ctx, ""); + + hash = crypt_hash_password(password); + if (!hash) + return -1; + + return crypt_set_password_hash(ctx, hash); +} + +char *crypt_get_hash(void *ctx) +{ + struct spwd *shadow; + + shadow = getspnam("root"); + if (!shadow) { + pb_log("Could not find root shadow\n"); + return false; + } + + return talloc_strdup(ctx, shadow->sp_pwdp); +} + +bool crypt_check_password(const char *password) +{ + struct spwd *shadow; + char *hash; + + shadow = getspnam("root"); + if (!shadow) { + pb_log("Could not find root shadow\n"); + return false; + } + + hash = crypt(password ? : "", shadow->sp_pwdp); + if (!hash) { + pb_log("Could not create hash, %m\n"); + return false; + } + + return strncmp(shadow->sp_pwdp, hash, strlen(shadow->sp_pwdp)) == 0; +} diff --git a/lib/crypt/crypt.h b/lib/crypt/crypt.h new file mode 100644 index 00000000..4b242f0c --- /dev/null +++ b/lib/crypt/crypt.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef CRYPT_H +#define CRYPT_H + +#include "config.h" + +#ifdef CRYPT_SUPPORT + +char *crypt_get_hash(void *ctx); +bool crypt_check_password(const char *password); +int crypt_set_password(void *ctx, const char *password); +int crypt_set_password_hash(void *ctx, const char *hash); + +#else + +static inline char *crypt_get_hash(void *ctx __attribute__((unused))) +{ + return NULL; +} +static inline bool crypt_check_password( + const char *password __attribute__((unused))) +{ + return false; +} +static inline int crypt_set_password(void *ctx __attribute__((unused)), + const char *password __attribute__((unused))) +{ + return -1; +} +static inline int crypt_set_password_hash(void *ctx __attribute__((unused)), + const char *hash __attribute__((unused))) +{ + return -1; +} + +#endif +#endif /* CRYPT_H */