Coverage Report

Created: 2026-09-06 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/httpd/srclib/apr/crypto/apr_passwd.c
Line
Count
Source
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
 * contributor license agreements.  See the NOTICE file distributed with
3
 * this work for additional information regarding copyright ownership.
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
5
 * (the "License"); you may not use this file except in compliance with
6
 * the License.  You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include "apr_strings.h"
18
#include "apr_md5.h"
19
#include "apr_lib.h"
20
#include "apr_private.h"
21
#include "apr_sha1.h"
22
#include "crypt_blowfish.h"
23
24
#if APR_HAVE_STRING_H
25
#include <string.h>
26
#endif
27
#if APR_HAVE_CRYPT_H
28
#include <crypt.h>
29
#endif
30
#if APR_HAVE_UNISTD_H
31
#include <unistd.h>
32
#endif
33
#if APR_HAVE_PTHREAD_H
34
#include <pthread.h>
35
#endif
36
#if APR_HAVE_STDLIB_H
37
#include <stdlib.h>
38
#endif
39
40
static const char * const apr1_id = "$apr1$";
41
42
#if !defined(WIN32) && !defined(NETWARE)
43
#if defined(APU_CRYPT_THREADSAFE) || !APR_HAS_THREADS || \
44
    defined(CRYPT_R_CRYPTD) || defined(CRYPT_R_STRUCT_CRYPT_DATA)
45
46
#define crypt_mutex_lock()
47
#define crypt_mutex_unlock()
48
49
#elif APR_HAVE_PTHREAD_H && defined(PTHREAD_MUTEX_INITIALIZER)
50
51
static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER;
52
static void crypt_mutex_lock(void)
53
{
54
    pthread_mutex_lock(&crypt_mutex);
55
}
56
57
static void crypt_mutex_unlock(void)
58
{
59
    pthread_mutex_unlock(&crypt_mutex);
60
}
61
62
#elif defined(OS2)
63
64
static HMTX crypt_mutex = 0;
65
static void crypt_mutex_lock()
66
{
67
    if (crypt_mutex == 0) {
68
        /* Prevent race condition where two threads could try to create the
69
         * mutex concurrently
70
         */
71
        DosEnterCritSec();
72
73
        if (crypt_mutex == 0) {
74
            DosCreateMutexSem(NULL, &crypt_mutex, 0, FALSE);
75
        }
76
77
        DosExitCritSec();
78
    }
79
80
    DosRequestMutexSem(crypt_mutex, SEM_INDEFINITE_WAIT);
81
}
82
83
static void crypt_mutex_unlock()
84
{
85
    DosReleaseMutexSem(crypt_mutex);
86
}
87
88
#else
89
90
#error apr_password_validate() is not threadsafe.  rebuild APR without thread support.
91
92
#endif
93
#endif
94
95
#if defined(WIN32) || defined(__ANDROID__)
96
#define CRYPT_MISSING 1
97
#else
98
#define CRYPT_MISSING 0
99
#endif
100
101
/*
102
 * Validate a plaintext password against a smashed one.  Uses either
103
 * crypt() (if available) or apr_md5_encode() or apr_sha1_base64(), depending
104
 * upon the format of the smashed input password.  Returns APR_SUCCESS if
105
 * they match, or APR_EMISMATCH if they don't.  If the platform doesn't
106
 * support crypt, then the default check is against a clear text string.
107
 */
108
APR_DECLARE(apr_status_t) apr_password_validate(const char *passwd,
109
                                                const char *hash)
110
0
{
111
0
    char sample[200];
112
0
#if !CRYPT_MISSING
113
0
    char *crypt_pw;
114
0
#endif
115
116
0
    if ((apr_strneq_timingsafe(hash, "$2a$", 4) | /* test both */
117
0
         apr_strneq_timingsafe(hash, "$2y$", 4))) {
118
        /*
119
         * The hash was created using [apr_]bcrypt encoding.
120
         */
121
0
        if (_crypt_blowfish_rn(passwd, hash, sample, sizeof(sample)) == NULL)
122
0
            return APR_FROM_OS_ERROR(errno);
123
0
    }
124
0
    else if (apr_strneq_timingsafe(hash, apr1_id, strlen(apr1_id))) {
125
        /*
126
         * The hash was created using our custom algorithm.
127
         */
128
0
        apr_md5_encode(passwd, hash, sample, sizeof(sample));
129
0
    }
130
0
    else if (apr_strneq_timingsafe(hash, APR_SHA1PW_ID, APR_SHA1PW_IDLEN)) {
131
        /*
132
         * The hash is a (naked) SHA1.
133
         */
134
0
        apr_sha1_base64(passwd, (int)strlen(passwd), sample);
135
0
    }
136
0
    else {
137
        /*
138
         * It's not our algorithm, so feed it to crypt() if possible.
139
         */
140
#if CRYPT_MISSING
141
        return apr_streq_timingsafe(hash, passwd) ? APR_SUCCESS : APR_EMISMATCH;
142
#elif defined(CRYPT_R_CRYPTD)
143
        apr_status_t rv;
144
        CRYPTD *buffer = malloc(sizeof(*buffer));
145
146
        if (buffer == NULL)
147
            return APR_ENOMEM;
148
        crypt_pw = crypt_r(passwd, hash, buffer);
149
        if (!crypt_pw)
150
            rv = APR_EMISMATCH;
151
        else
152
            rv = apr_streq_timingsafe(hash, crypt_pw) ? APR_SUCCESS : APR_EMISMATCH;
153
        free(buffer);
154
        return rv;
155
#elif defined(CRYPT_R_STRUCT_CRYPT_DATA)
156
        apr_status_t rv;
157
0
        struct crypt_data *buffer = malloc(sizeof(*buffer));
158
159
0
        if (buffer == NULL)
160
0
            return APR_ENOMEM;
161
162
0
#ifdef __GLIBC_PREREQ
163
        /*
164
         * For not too old glibc (>= 2.3.2), it's enough to set
165
         * buffer.initialized = 0. For < 2.3.2 and for other platforms,
166
         * we need to zero the whole struct.
167
         */
168
0
#if __GLIBC_PREREQ(2,4)
169
0
#define USE_CRYPT_DATA_INITALIZED
170
0
#endif
171
0
#endif
172
173
0
#ifdef USE_CRYPT_DATA_INITALIZED
174
0
        buffer->initialized = 0;
175
#else
176
        memset(buffer, 0, sizeof(*buffer));
177
#endif
178
179
0
        crypt_pw = crypt_r(passwd, hash, buffer);
180
0
        if (!crypt_pw)
181
0
            rv = APR_EMISMATCH;
182
0
        else
183
0
            rv = apr_streq_timingsafe(hash, crypt_pw) ? APR_SUCCESS : APR_EMISMATCH;
184
0
        free(buffer);
185
0
        return rv;
186
#else
187
        /* Do a bit of sanity checking since we know that crypt_r()
188
         * should always be used for threaded builds on AIX, and
189
         * problems in configure logic can result in the wrong
190
         * choice being made.
191
         */
192
#if defined(_AIX) && APR_HAS_THREADS
193
#error Configuration error!  crypt_r() should have been selected!
194
#endif
195
        {
196
            apr_status_t rv;
197
198
            /* Handle thread safety issues by holding a mutex around the
199
             * call to crypt().
200
             */
201
            crypt_mutex_lock();
202
            crypt_pw = crypt(passwd, hash);
203
            if (!crypt_pw) {
204
                rv = APR_EMISMATCH;
205
            }
206
            else {
207
                rv = apr_streq_timingsafe(hash, crypt_pw) ? APR_SUCCESS : APR_EMISMATCH;
208
            }
209
            crypt_mutex_unlock();
210
            return rv;
211
        }
212
#endif
213
0
    }
214
0
    return apr_streq_timingsafe(hash, sample) ? APR_SUCCESS : APR_EMISMATCH;
215
0
}
216
217
static const char * const bcrypt_id = "$2y$";
218
APR_DECLARE(apr_status_t) apr_bcrypt_encode(const char *pw,
219
                                            unsigned int count,
220
                                            const unsigned char *salt,
221
                                            apr_size_t salt_len,
222
                                            char *out, apr_size_t out_len)
223
0
{
224
0
    char setting[40];
225
0
    if (_crypt_gensalt_blowfish_rn(bcrypt_id, count, (const char *)salt,
226
0
                                   salt_len, setting, sizeof(setting)) == NULL)
227
0
        return APR_FROM_OS_ERROR(errno);
228
0
    if (_crypt_blowfish_rn(pw, setting, out, out_len) == NULL)
229
0
        return APR_FROM_OS_ERROR(errno);
230
0
    return APR_SUCCESS;
231
0
}