/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "apr_strings.h" #include "apr_md5.h" #include "apr_lib.h" #include "apr_private.h" #include "apr_sha1.h" #include "crypt_blowfish.h" #if APR_HAVE_STRING_H #include #endif #if APR_HAVE_CRYPT_H #include #endif #if APR_HAVE_UNISTD_H #include #endif #if APR_HAVE_PTHREAD_H #include #endif #if APR_HAVE_STDLIB_H #include #endif static const char * const apr1_id = "$apr1$"; #if !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) #if defined(APU_CRYPT_THREADSAFE) || !APR_HAS_THREADS || \ defined(CRYPT_R_CRYPTD) || defined(CRYPT_R_STRUCT_CRYPT_DATA) #define crypt_mutex_lock() #define crypt_mutex_unlock() #elif APR_HAVE_PTHREAD_H && defined(PTHREAD_MUTEX_INITIALIZER) static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER; static void crypt_mutex_lock(void) { pthread_mutex_lock(&crypt_mutex); } static void crypt_mutex_unlock(void) { pthread_mutex_unlock(&crypt_mutex); } #elif defined(OS2) static HMTX crypt_mutex = 0; static void crypt_mutex_lock() { if (crypt_mutex == 0) { /* Prevent race condition where two threads could try to create the * mutex concurrently */ DosEnterCritSec(); if (crypt_mutex == 0) { DosCreateMutexSem(NULL, &crypt_mutex, 0, FALSE); } DosExitCritSec(); } DosRequestMutexSem(crypt_mutex, SEM_INDEFINITE_WAIT); } static void crypt_mutex_unlock() { DosReleaseMutexSem(crypt_mutex); } #else #error apr_password_validate() is not threadsafe. rebuild APR without thread support. #endif #endif #if defined(WIN32) || defined(BEOS) || defined(NETWARE) || defined(__ANDROID__) #define CRYPT_MISSING 1 #else #define CRYPT_MISSING 0 #endif /* * Validate a plaintext password against a smashed one. Uses either * crypt() (if available) or apr_md5_encode() or apr_sha1_base64(), depending * upon the format of the smashed input password. Returns APR_SUCCESS if * they match, or APR_EMISMATCH if they don't. If the platform doesn't * support crypt, then the default check is against a clear text string. */ APR_DECLARE(apr_status_t) apr_password_validate(const char *passwd, const char *hash) { char sample[200]; #if !CRYPT_MISSING char *crypt_pw; #endif if (hash[0] == '$' && hash[1] == '2' && (hash[2] == 'a' || hash[2] == 'y') && hash[3] == '$') { if (_crypt_blowfish_rn(passwd, hash, sample, sizeof(sample)) == NULL) return APR_FROM_OS_ERROR(errno); } else if (!strncmp(hash, apr1_id, strlen(apr1_id))) { /* * The hash was created using our custom algorithm. */ apr_md5_encode(passwd, hash, sample, sizeof(sample)); } else if (!strncmp(hash, APR_SHA1PW_ID, APR_SHA1PW_IDLEN)) { apr_sha1_base64(passwd, (int)strlen(passwd), sample); } else { /* * It's not our algorithm, so feed it to crypt() if possible. */ #if CRYPT_MISSING return (strcmp(passwd, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH; #elif defined(CRYPT_R_CRYPTD) apr_status_t rv; CRYPTD *buffer = malloc(sizeof(*buffer)); if (buffer == NULL) return APR_ENOMEM; crypt_pw = crypt_r(passwd, hash, buffer); if (!crypt_pw) rv = APR_EMISMATCH; else rv = (strcmp(crypt_pw, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH; free(buffer); return rv; #elif defined(CRYPT_R_STRUCT_CRYPT_DATA) apr_status_t rv; struct crypt_data *buffer = malloc(sizeof(*buffer)); if (buffer == NULL) return APR_ENOMEM; #ifdef __GLIBC_PREREQ /* * For not too old glibc (>= 2.3.2), it's enough to set * buffer.initialized = 0. For < 2.3.2 and for other platforms, * we need to zero the whole struct. */ #if __GLIBC_PREREQ(2,4) #define USE_CRYPT_DATA_INITALIZED #endif #endif #ifdef USE_CRYPT_DATA_INITALIZED buffer->initialized = 0; #else memset(buffer, 0, sizeof(*buffer)); #endif crypt_pw = crypt_r(passwd, hash, buffer); if (!crypt_pw) rv = APR_EMISMATCH; else rv = (strcmp(crypt_pw, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH; free(buffer); return rv; #else /* Do a bit of sanity checking since we know that crypt_r() * should always be used for threaded builds on AIX, and * problems in configure logic can result in the wrong * choice being made. */ #if defined(_AIX) && APR_HAS_THREADS #error Configuration error! crypt_r() should have been selected! #endif { apr_status_t rv; /* Handle thread safety issues by holding a mutex around the * call to crypt(). */ crypt_mutex_lock(); crypt_pw = crypt(passwd, hash); if (!crypt_pw) { rv = APR_EMISMATCH; } else { rv = (strcmp(crypt_pw, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH; } crypt_mutex_unlock(); return rv; } #endif } return (strcmp(sample, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH; } static const char * const bcrypt_id = "$2y$"; APR_DECLARE(apr_status_t) apr_bcrypt_encode(const char *pw, unsigned int count, const unsigned char *salt, apr_size_t salt_len, char *out, apr_size_t out_len) { char setting[40]; if (_crypt_gensalt_blowfish_rn(bcrypt_id, count, (const char *)salt, salt_len, setting, sizeof(setting)) == NULL) return APR_FROM_OS_ERROR(errno); if (_crypt_blowfish_rn(pw, setting, out, out_len) == NULL) return APR_FROM_OS_ERROR(errno); return APR_SUCCESS; }