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