]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/libcrypt-util.c
meson: test if we have libcrypt_ra
[thirdparty/systemd.git] / src / shared / libcrypt-util.c
CommitLineData
42f3b2f9
LP
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
83764f8d
ZJS
3#if HAVE_CRYPT_H
4/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
5 * removed from glibc at some point. As part of the removal, defines for
6 * crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
7 *
8 * Newer versions of glibc (v2.0+) already ship crypt.h with a definition
9 * of crypt(3) as well, so we simply include it if it is present. MariaDB,
10 * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
11 * same way since ages without any problems.
12 */
13# include <crypt.h>
14#else
15# include <unistd.h>
16#endif
17
42f3b2f9
LP
18#include <errno.h>
19#include <stdlib.h>
20
21#include "alloc-util.h"
0e98d17e 22#include "errno-util.h"
42f3b2f9
LP
23#include "libcrypt-util.h"
24#include "log.h"
25#include "macro.h"
a937ce2d 26#include "memory-util.h"
42f3b2f9
LP
27#include "missing_stdlib.h"
28#include "random-util.h"
29#include "string-util.h"
30#include "strv.h"
31
32int make_salt(char **ret) {
33
34#ifdef XCRYPT_VERSION_MAJOR
35 const char *e;
36 char *salt;
37
38 /* If we have libxcrypt we default to the "preferred method" (i.e. usually yescrypt), and generate it
39 * with crypt_gensalt_ra(). */
40
41 e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
42 if (!e)
43 e = crypt_preferred_method();
44
45 log_debug("Generating salt for hash prefix: %s", e);
46
47 salt = crypt_gensalt_ra(e, 0, NULL, 0);
48 if (!salt)
49 return -errno;
50
51 *ret = salt;
52 return 0;
53#else
54 /* If libxcrypt is not used, we use SHA512 and generate the salt on our own since crypt_gensalt_ra()
55 * is not available. */
56
57 static const char table[] =
58 "abcdefghijklmnopqrstuvwxyz"
59 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
60 "0123456789"
61 "./";
62
63 uint8_t raw[16];
64 char *salt, *j;
65 size_t i;
66 int r;
67
68 /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
69 * SHA512, i.e. is legacy-free and minimizes our deps. */
70
71 assert_cc(sizeof(table) == 64U + 1U);
72
73 /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
74 r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
75 if (r < 0)
76 return r;
77
78 salt = new(char, 3+sizeof(raw)+1+1);
79 if (!salt)
80 return -ENOMEM;
81
82 /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
83 j = stpcpy(salt, "$6$");
84 for (i = 0; i < sizeof(raw); i++)
85 j[i] = table[raw[i] & 63];
86 j[i++] = '$';
87 j[i] = 0;
88
89 *ret = salt;
90 return 0;
91#endif
92}
64aa2622 93
a937ce2d 94int hash_password_full(const char *password, void **cd_data, int *cd_size, char **ret) {
0e98d17e 95 _cleanup_free_ char *salt = NULL;
a937ce2d 96 _cleanup_(erase_and_freep) void *_cd_data = NULL;
0e98d17e 97 char *p;
a937ce2d
ZJS
98 int r, _cd_size = 0;
99
100 assert(!!cd_data == !!cd_size);
0e98d17e
ZJS
101
102 r = make_salt(&salt);
103 if (r < 0)
104 return log_debug_errno(r, "Failed to generate salt: %m");
105
106 errno = 0;
a937ce2d 107 p = crypt_ra(password, salt, cd_data ?: &_cd_data, cd_size ?: &_cd_size);
0e98d17e
ZJS
108 if (!p)
109 return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)),
a937ce2d 110 "crypt_ra() failed: %m");
0e98d17e
ZJS
111
112 p = strdup(p);
113 if (!p)
114 return -ENOMEM;
115
116 *ret = p;
117 return 0;
118}
119
8f796e40
ZJS
120bool looks_like_hashed_password(const char *s) {
121 /* Returns false if the specified string is certainly not a hashed UNIX password. crypt(5) lists
122 * various hashing methods. We only reject (return false) strings which are documented to have
123 * different meanings.
124 *
125 * In particular, we allow locked passwords, i.e. strings starting with "!", including just "!",
126 * i.e. the locked empty password. See also fc58c0c7bf7e4f525b916e3e5be0de2307fef04e.
127 */
128 if (!s)
64aa2622
LP
129 return false;
130
8f796e40
ZJS
131 s += strspn(s, "!"); /* Skip (possibly duplicated) locking prefix */
132
133 return !STR_IN_SET(s, "x", "*");
64aa2622 134}
2ae297fe
ZJS
135
136int test_password_one(const char *hashed_password, const char *password) {
999b49c8
ZJS
137 _cleanup_(erase_and_freep) void *cd_data = NULL;
138 int cd_size = 0;
2ae297fe 139 const char *k;
2ae297fe
ZJS
140
141 errno = 0;
999b49c8
ZJS
142 k = crypt_ra(password, hashed_password, &cd_data, &cd_size);
143 if (!k)
2ae297fe 144 return errno_or_else(EINVAL);
2ae297fe 145
999b49c8 146 return streq(k, hashed_password);
2ae297fe
ZJS
147}
148
149int test_password_many(char **hashed_password, const char *password) {
150 char **hpw;
151 int r;
152
153 STRV_FOREACH(hpw, hashed_password) {
154 r = test_password_one(*hpw, password);
155 if (r < 0)
156 return r;
157 if (r > 0)
158 return true;
159 }
160
161 return false;
162}