1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
#include <replace.h>
#include "data_blob.h"
#include "discard.h"
#include <talloc.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "util_crypt.h"
static int crypt_as_best_we_can(TALLOC_CTX *mem_ctx,
const char *phrase,
const char *setting,
const char **hashp)
{
int ret = 0;
const char *hash = NULL;
#if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT_RN)
struct crypt_data crypt_data = {
.initialized = 0 /* working storage used by crypt */
};
#endif
/*
* crypt_r() and crypt() may return a null pointer upon error
* depending on how libcrypt was configured, so we prefer
* crypt_rn() from libcrypt / libxcrypt which always returns
* NULL on error.
*
* POSIX specifies returning a null pointer and setting
* errno.
*
* RHEL 7 (which does not use libcrypt / libxcrypt) returns a
* non-NULL pointer from crypt_r() on success but (always?)
* sets errno during internal processing in the NSS crypto
* subsystem.
*
* By preferring crypt_rn we avoid the 'return non-NULL but
* set-errno' that we otherwise cannot tell apart from the
* RHEL 7 behaviour.
*/
errno = 0;
#ifdef HAVE_CRYPT_RN
hash = crypt_rn(phrase, setting,
&crypt_data,
sizeof(crypt_data));
#elif HAVE_CRYPT_R
hash = crypt_r(phrase, setting, &crypt_data);
#else
/*
* No crypt_r falling back to crypt, which is NOT thread safe
* Thread safety MT-Unsafe race:crypt
*/
hash = crypt(phrase, setting);
#endif
/*
* On error, crypt() and crypt_r() may return a null pointer,
* or a pointer to an invalid hash beginning with a '*'.
*/
ret = errno;
errno = 0;
if (hash == NULL || hash[0] == '*') {
if (ret == 0) {
/* this is annoying */
ret = ENOTRECOVERABLE;
}
}
if (ret != 0) {
return ret;
}
*hashp = talloc_strdup(mem_ctx, hash);
if (*hashp == NULL) {
ret = -1;
}
return ret;
}
int talloc_crypt_blob(TALLOC_CTX *mem_ctx,
const char *phrase,
const char *setting,
DATA_BLOB *blob)
{
const char *hash = NULL;
int ret = crypt_as_best_we_can(mem_ctx, phrase, setting, &hash);
if (ret != 0) {
blob->data = NULL;
blob->length = 0;
return ret;
}
blob->length = strlen(hash);
blob->data = discard_const_p(uint8_t, hash);
if (blob->data == NULL) {
return ENOMEM;
}
return 0;
}
char *talloc_crypt_errstring(TALLOC_CTX *mem_ctx, int error)
{
char buf[1024];
int err;
if (error == ERANGE) {
return talloc_strdup(
mem_ctx,
"Password exceeds maximum length allowed for crypt() hashing");
}
if (error == ENOTRECOVERABLE) {
/* probably weird RHEL7 crypt, see crypt_as_best_we_can() */
goto unknown;
}
err = strerror_r(error, buf, sizeof(buf));
if (err != 0) {
goto unknown;
}
return talloc_strndup(mem_ctx, buf, sizeof(buf));
unknown:
return talloc_strdup(mem_ctx, "Unknown error");
}
|