Esempio n. 1
0
// ---------------------------------------------------------------------------
// This is the wrapper for the function apr_md5_encode(), included from
// apr_md5.h. From within Python, this wrapper will be available as
//
//   aprmd5.md5_encode()
//
// Generates a salted crypt-style hash. For input "foo" and salt "mYJd83wW",
// the result is this: "$apr1$mYJd83wW$IO.6aK3G0d4mHxcImhPX50".
//
// Parameters of the Python function:
// - password: a string object that contains the password to be encrypted
// - salt: a string object that contains the salt to be used for encryption; the
//   htpasswd command line utility has been observed to use 8 character salts,
//   which at the same time is the maximum length that makes any difference (it
//   is possible to provide salts that are longer, but all characters beyond the
//   8th character will be ignored); the salt may be less than 8 characters
//   (even zero)
//
// Return value of the Python function:
// - A string object that contains the encrypted password in the format
//   "$apr1$salt$encrypted-password". Note that the prefix "$apr1$" is a
//   constant that distinguishes this form of crypt-style hash from other
//   crypt-style hashes, which use other prefixes (e.g. the original DES crypt
//   does not have a prefix at all, while "$1$" denotes yet another MD5-based
//   hash that is, for instance, implemented by the glibc function crypt(3)).
// ---------------------------------------------------------------------------
PyObject*
aprmd5_md5_encode(PyObject* self, PyObject* args)
{
  // Both the input and the salt must be str() objects, from which we can get
  // NULL-terminated char*.
  // Note: This is true for both Python 2.6 and Python 3
  const char* input;
  const char* salt;
  if (! PyArg_ParseTuple(args, "ss", &input, &salt))
    return NULL;

  // Generate the hash
  apr_size_t resultLen = 6 + strlen(salt) + 1 + 22 + 1;  // 6 = $apr1$
                                                         // 1 = $ (terminating the salt)
                                                         // 22 = hash
                                                         // 1 = terminating null byte
  char result[resultLen];
  // +1 to resultLen because, for some unknown reason, apr_md5_encode() wants
  // an additional byte
  apr_status_t status = apr_md5_encode(input, salt, result, resultLen + 1);
  if (APR_SUCCESS != status)
  {
    PyErr_SetString(PyExc_RuntimeError, "apr_md5_encode() returned status code != 0");
    return NULL;
  }

  // Return the result; the Python system becomes responsible for the object
  // returned by Py_BuildValue().
  return Py_BuildValue("s", result);
}
Esempio n. 2
0
static void test_md5pass(abts_case *tc, void *data)
{
    const char *pass = "******", *salt = "sardine";
    char hash[100];

    apr_md5_encode(pass, salt, hash, sizeof hash);

    APR_ASSERT_SUCCESS(tc, "MD5 password validated",
                       apr_password_validate(pass, hash));
}
Esempio n. 3
0
static void test_md5pass(abts_case *tc, void *data)
{
    const char *pass = "******", *salt = "sardine";
    const char *pass2 = "hellojed2";
    char hash[100];

    apr_md5_encode(pass, salt, hash, sizeof hash);

    apr_assert_success(tc, "MD5 password validated",
                       apr_password_validate(pass, hash));
    APR_ASSERT_FAILURE(tc, "wrong MD5 password should not validate",
                       apr_password_validate(pass2, hash));
}
Esempio n. 4
0
static apr_status_t htdbm_make(htdbm_t *htdbm)
{
    char cpw[MAX_STRING_LEN];
    char salt[9];
#if (!(defined(WIN32) || defined(NETWARE)))
    char *cbuf;
#endif

    switch (htdbm->alg) {
        case ALG_APSHA:
            /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
            apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
        break;

        case ALG_APMD5:
            (void) srand((int) time((time_t *) NULL));
            to64(&salt[0], rand(), 8);
            salt[8] = '\0';
            apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
                            cpw, sizeof(cpw));
        break;
        case ALG_PLAIN:
            /* XXX this len limitation is not in sync with any HTTPd len. */
            apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
#if (!(defined(WIN32) || defined(TPF) || defined(NETWARE)))
            fprintf(stderr, "Warning: Plain text passwords aren't supported by the "
                    "server on this platform!\n");
#endif
        break;
#if (!(defined(WIN32) || defined(TPF) || defined(NETWARE)))
        case ALG_CRYPT:
            (void) srand((int) time((time_t *) NULL));
            to64(&salt[0], rand(), 8);
            salt[8] = '\0';
            cbuf = crypt(htdbm->userpass, salt);
            if (cbuf == NULL) {
                char errbuf[128];
                
                fprintf(stderr, "crypt() failed: %s\n", 
                        apr_strerror(errno, errbuf, sizeof errbuf));
                exit(ERR_PWMISMATCH);
            }
            apr_cpystrn(cpw, cbuf, sizeof(cpw) - 1);
            fprintf(stderr, "CRYPT is now deprecated, use MD5 instead!\n");
#endif
        default:
        break;
    }
    htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
    return APR_SUCCESS;
}
Esempio n. 5
0
/*
 * 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 !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
    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 defined(WIN32) || defined(BEOS) || defined(NETWARE)
        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;
}
Esempio n. 6
0
/*
 * 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 !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
    char *crypt_pw;
#endif
    if (hash[0] == '$') {
        if (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 defined(WIN32) || defined(BEOS) || defined(NETWARE)
        return (strcmp(passwd, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH;
#elif defined(CRYPT_R_CRYPTD)
        CRYPTD buffer;

        crypt_pw = crypt_r(passwd, hash, &buffer);
        if (!crypt_pw) {
            return APR_EMISMATCH;
        }
        return (strcmp(crypt_pw, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH;
#elif defined(CRYPT_R_STRUCT_CRYPT_DATA)
        struct crypt_data buffer;

#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2,4)
        buffer.initialized = 0;
#else
        /*
         * glibc before 2.3.2 had a bug that required clearing the
         * whole struct
         */
        memset(&buffer, 0, sizeof(buffer));
#endif
        crypt_pw = crypt_r(passwd, hash, &buffer);
        if (!crypt_pw) {
            return APR_EMISMATCH;
        }
        return (strcmp(crypt_pw, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH;
#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;
}
Esempio n. 7
0
/*
 * Make a password record from the given information.  A zero return
 * indicates success; on failure, ctx->errstr points to the error message.
 */
int mkhash(struct passwd_ctx *ctx)
{
    char *pw;
    char pwin[MAX_STRING_LEN];
    char salt[16];
    apr_status_t rv;
    int ret = 0;
#if CRYPT_ALGO_SUPPORTED
    char *cbuf;
#endif

    if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) {
        apr_file_printf(errfile,
                        "Warning: Ignoring -C argument for this algorithm." NL);
    }

    if (ctx->passwd != NULL) {
        pw = ctx->passwd;
    }
    else {
        if ((ret = get_password(ctx)) != 0)
            return ret;
        pw = pwin;
    }

    switch (ctx->alg) {
    case ALG_APSHA:
        /* XXX out >= 28 + strlen(sha1) chars - fixed len SHA */
        apr_sha1_base64(pw, strlen(pw), ctx->out);
        break;

    case ALG_APMD5:
        ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
        if (ret != 0)
            break;
        rv = apr_md5_encode(pw, salt, ctx->out, ctx->out_len);
        if (rv != APR_SUCCESS) {
            ctx->errstr = apr_psprintf(ctx->pool,
                                       "could not encode password: %pm", &rv);
            ret = ERR_GENERAL;
        }
        break;

    case ALG_PLAIN:
        /* XXX this len limitation is not in sync with any HTTPd len. */
        apr_cpystrn(ctx->out, pw, ctx->out_len);
        break;

#if CRYPT_ALGO_SUPPORTED
    case ALG_CRYPT:
        ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
        if (ret != 0)
            break;
        cbuf = crypt(pw, salt);
        if (cbuf == NULL) {
            rv = APR_FROM_OS_ERROR(errno);
            ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
            ret = ERR_PWMISMATCH;
            break;
        }

        apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
        if (strlen(pw) > 8) {
            char *truncpw = strdup(pw);
            truncpw[8] = '\0';
            if (!strcmp(ctx->out, crypt(truncpw, salt))) {
                apr_file_printf(errfile, "Warning: Password truncated to 8 "
                                "characters by CRYPT algorithm." NL);
            }
            memset(truncpw, '\0', strlen(pw));
            free(truncpw);
        }
        break;
#endif /* CRYPT_ALGO_SUPPORTED */

#if BCRYPT_ALGO_SUPPORTED
    case ALG_BCRYPT:
        rv = apr_generate_random_bytes((unsigned char*)salt, 16);
        if (rv != APR_SUCCESS) {
            ctx->errstr = apr_psprintf(ctx->pool, "Unable to generate random "
                                       "bytes: %pm", &rv);
            ret = ERR_RANDOM;
            break;
        }

        if (ctx->cost == 0)
            ctx->cost = BCRYPT_DEFAULT_COST;
        rv = apr_bcrypt_encode(pw, ctx->cost, (unsigned char*)salt, 16,
                               ctx->out, ctx->out_len);
        if (rv != APR_SUCCESS) {
            ctx->errstr = apr_psprintf(ctx->pool, "Unable to encode with "
                                       "bcrypt: %pm", &rv);
            ret = ERR_PWMISMATCH;
            break;
        }
        break;
#endif /* BCRYPT_ALGO_SUPPORTED */

    default:
        apr_file_printf(errfile, "mkhash(): BUG: invalid algorithm %d",
                        ctx->alg);
        abort();
    }
    memset(pw, '\0', strlen(pw));
    return ret;
}
Esempio n. 8
0
/*
 * Make a password record from the given information.  A zero return
 * indicates success; failure means that the output buffer contains an
 * error message instead.
 */
static int mkrecord(char *user, char *record, apr_size_t rlen, char *passwd,
                    int alg)
{
    char *pw;
    char cpw[120];
    char pwin[MAX_STRING_LEN];
    char pwv[MAX_STRING_LEN];
    char salt[9];
    apr_size_t bufsize;

    if (passwd != NULL) {
        pw = passwd;
    }
    else {
        bufsize = sizeof(pwin);
        if (apr_password_get("New password: "******"password too long (>%"
                         APR_SIZE_T_FMT ")", sizeof(pwin) - 1);
            return ERR_OVERFLOW;
        }
        bufsize = sizeof(pwv);
        apr_password_get("Re-type new password: "******"password verification error", (rlen - 1));
            return ERR_PWMISMATCH;
        }
        pw = pwin;
        memset(pwv, '\0', sizeof(pwin));
    }
    switch (alg) {

    case ALG_APSHA:
        /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
        apr_sha1_base64(pw,strlen(pw),cpw);
        break;

    case ALG_APMD5:
        if (seed_rand()) {
            break;
        }
        generate_salt(&salt[0], 8);
        salt[8] = '\0';

        apr_md5_encode((const char *)pw, (const char *)salt,
                     cpw, sizeof(cpw));
        break;

    case ALG_PLAIN:
        /* XXX this len limitation is not in sync with any HTTPd len. */
        apr_cpystrn(cpw,pw,sizeof(cpw));
        break;

#if (!(defined(WIN32) || defined(NETWARE)))
    case ALG_CRYPT:
    default:
        if (seed_rand()) {
            break;
        }
        to64(&salt[0], rand(), 8);
        salt[8] = '\0';

        apr_cpystrn(cpw, crypt(pw, salt), sizeof(cpw) - 1);
        if (strlen(pw) > 8) {
            char *truncpw = strdup(pw);
            truncpw[8] = '\0';
            if (!strcmp(cpw, crypt(truncpw, salt))) {
                apr_file_printf(errfile, "Warning: Password truncated to 8 characters "
                                "by CRYPT algorithm." NL);
            }
            free(truncpw);
        }
        break;
#endif
    }
    memset(pw, '\0', strlen(pw));

    /*
     * Check to see if the buffer is large enough to hold the username,
     * hash, and delimiters.
     */
    if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) {
        apr_cpystrn(record, "resultant record too long", (rlen - 1));
        return ERR_OVERFLOW;
    }
    strcpy(record, user);
    strcat(record, ":");
    strcat(record, cpw);
    strcat(record, "\n");
    return 0;
}