]>
Commit | Line | Data |
---|---|---|
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 | ||
17 | int 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 | 79 | int 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 |
105 | bool 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 | |
121 | int 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 | ||
134 | int 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 | } |