diff options
Diffstat (limited to 'crypto/apr_passwd.c')
-rw-r--r-- | crypto/apr_passwd.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/crypto/apr_passwd.c b/crypto/apr_passwd.c new file mode 100644 index 0000000..c961de2 --- /dev/null +++ b/crypto/apr_passwd.c @@ -0,0 +1,200 @@ +/* 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_sha1.h" +#include "apu_config.h" +#include "crypt_blowfish.h" + +#if APR_HAVE_STRING_H +#include <string.h> +#endif +#if APR_HAVE_CRYPT_H +#include <crypt.h> +#endif +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif +#if APR_HAVE_PTHREAD_H +#include <pthread.h> +#endif +#if APR_HAVE_STDLIB_H +#include <stdlib.h> +#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); +} + +#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. + */ +APU_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$"; +APU_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; +} |