📄 cram.c
字号:
/* * cram.c : Minimal standalone CRAM-MD5 implementation * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#define APR_WANT_STRFUNC#define APR_WANT_STDIO#include <apr_want.h>#include <apr_general.h>#include <apr_strings.h>#include <apr_network_io.h>#include <apr_time.h>#include <apr_md5.h>#include "svn_types.h"#include "svn_string.h"#include "svn_error.h"#include "svn_ra_svn.h"#include "svn_config.h"#include "svn_private_config.h"#include "ra_svn.h"static int hex_to_int(char c){ return (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : -1;}static char int_to_hex(int v){ return (v < 10) ? '0' + v : 'a' + (v - 10);}static svn_boolean_t hex_decode(unsigned char *hashval, const char *hexval){ int i, h1, h2; for (i = 0; i < APR_MD5_DIGESTSIZE; i++) { h1 = hex_to_int(hexval[2 * i]); h2 = hex_to_int(hexval[2 * i + 1]); if (h1 == -1 || h2 == -1) return FALSE; hashval[i] = (h1 << 4) | h2; } return TRUE;}static void hex_encode(char *hexval, const unsigned char *hashval){ int i; for (i = 0; i < APR_MD5_DIGESTSIZE; i++) { hexval[2 * i] = int_to_hex((hashval[i] >> 4) & 0xf); hexval[2 * i + 1] = int_to_hex(hashval[i] & 0xf); }}static void compute_digest(unsigned char *digest, const char *challenge, const char *password){ unsigned char secret[64]; apr_size_t len = strlen(password), i; apr_md5_ctx_t ctx; /* Munge the password into a 64-byte secret. */ memset(secret, 0, sizeof(secret)); if (len <= sizeof(secret)) memcpy(secret, password, len); else apr_md5(secret, password, len); /* Compute MD5(secret XOR opad, MD5(secret XOR ipad, challenge)), * where ipad is a string of 0x36 and opad is a string of 0x5c. */ for (i = 0; i < sizeof(secret); i++) secret[i] ^= 0x36; apr_md5_init(&ctx); apr_md5_update(&ctx, secret, sizeof(secret)); apr_md5_update(&ctx, challenge, strlen(challenge)); apr_md5_final(digest, &ctx); for (i = 0; i < sizeof(secret); i++) secret[i] ^= (0x36 ^ 0x5c); apr_md5_init(&ctx); apr_md5_update(&ctx, secret, sizeof(secret)); apr_md5_update(&ctx, digest, APR_MD5_DIGESTSIZE); apr_md5_final(digest, &ctx);}/* Fail the authentication, from the server's perspective. */static svn_error_t *fail(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *msg){ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure", msg)); return svn_ra_svn_flush(conn, pool);}/* If we can, make the nonce with random bytes. If we can't... well, * it just has to be different each time. The current time isn't * absolutely guaranteed to be different for each connection, but it * should prevent replay attacks in practice. */static apr_status_t make_nonce(apr_uint64_t *nonce){#if APR_HAS_RANDOM return apr_generate_random_bytes((unsigned char *) nonce, sizeof(*nonce));#else *nonce = apr_time_now(); return APR_SUCCESS;#endif}svn_error_t *svn_ra_svn_cram_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_config_t *pwdb, const char **user, svn_boolean_t *success){ apr_status_t status; apr_uint64_t nonce; char hostbuf[APRMAXHOSTLEN + 1]; unsigned char cdigest[APR_MD5_DIGESTSIZE], sdigest[APR_MD5_DIGESTSIZE]; const char *challenge, *sep, *password; svn_ra_svn_item_t *item; svn_string_t *resp; *success = FALSE; /* Send a challenge. */ status = make_nonce(&nonce); if (!status) status = apr_gethostname(hostbuf, sizeof(hostbuf), pool); if (status) return fail(conn, pool, "Internal server error in authentication"); challenge = apr_psprintf(pool, "<%" APR_UINT64_T_FMT ".%" APR_TIME_T_FMT "@%s>", nonce, apr_time_now(), hostbuf); SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "step", challenge)); /* Read the client's response and decode it into *user and cdigest. */ SVN_ERR(svn_ra_svn_read_item(conn, pool, &item)); if (item->kind != SVN_RA_SVN_STRING) /* Very wrong; don't report failure */ return SVN_NO_ERROR; resp = item->u.string; sep = strrchr(resp->data, ' '); if (!sep || resp->len - (sep + 1 - resp->data) != APR_MD5_DIGESTSIZE * 2 || !hex_decode(cdigest, sep + 1)) return fail(conn, pool, "Malformed client response in authentication"); *user = apr_pstrmemdup(pool, resp->data, sep - resp->data); /* Verify the digest against the password in pwfile. */ svn_config_get(pwdb, &password, SVN_CONFIG_SECTION_USERS, *user, NULL); if (!password) return fail(conn, pool, "Username not found"); compute_digest(sdigest, challenge, password); if (memcmp(cdigest, sdigest, sizeof(sdigest)) != 0) return fail(conn, pool, "Password incorrect"); *success = TRUE; return svn_ra_svn_write_tuple(conn, pool, "w()", "success");}svn_error_t *svn_ra_svn__cram_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *user, const char *password, const char **message){ const char *status, *str, *reply; unsigned char digest[APR_MD5_DIGESTSIZE]; char hex[2 * APR_MD5_DIGESTSIZE + 1]; /* Read the server challenge. */ SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &str)); if (strcmp(status, "failure") == 0 && str) { *message = str; return SVN_NO_ERROR; } else if (strcmp(status, "step") != 0 || !str) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Unexpected server response to authentication")); /* Write our response. */ compute_digest(digest, str, password); hex_encode(hex, digest); hex[sizeof(hex) - 1] = '\0'; reply = apr_psprintf(pool, "%s %s", user, hex); SVN_ERR(svn_ra_svn_write_cstring(conn, pool, reply)); /* Read the success or failure response from the server. */ SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &str)); if (strcmp(status, "failure") == 0 && str) { *message = str; return SVN_NO_ERROR; } else if (strcmp(status, "success") != 0 || str) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Unexpected server response to authentication")); *message = NULL; return SVN_NO_ERROR;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -