]>
Commit | Line | Data |
---|---|---|
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 | ||
32 | int 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 | 94 | int 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 |
120 | bool 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 | |
136 | int 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 | ||
149 | int 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 | } |