Remove no-longer-used svn_*_get_mergeinfo_for_tree APIs.
[svn.git] / subversion / libsvn_ra_svn / cram.c
blob0b0829ab66c0dd0ccade793ad3140b2cd2e2252a
1 /*
2 * cram.c : Minimal standalone CRAM-MD5 implementation
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #define APR_WANT_STRFUNC
22 #define APR_WANT_STDIO
23 #include <apr_want.h>
24 #include <apr_general.h>
25 #include <apr_strings.h>
26 #include <apr_network_io.h>
27 #include <apr_time.h>
28 #include <apr_md5.h>
30 #include "svn_types.h"
31 #include "svn_string.h"
32 #include "svn_error.h"
33 #include "svn_ra_svn.h"
34 #include "svn_config.h"
35 #include "svn_private_config.h"
37 #include "ra_svn.h"
39 static int hex_to_int(char c)
41 return (c >= '0' && c <= '9') ? c - '0'
42 : (c >= 'a' && c <= 'f') ? c - 'a' + 10
43 : -1;
46 static char int_to_hex(int v)
48 return (v < 10) ? '0' + v : 'a' + (v - 10);
51 static svn_boolean_t hex_decode(unsigned char *hashval, const char *hexval)
53 int i, h1, h2;
55 for (i = 0; i < APR_MD5_DIGESTSIZE; i++)
57 h1 = hex_to_int(hexval[2 * i]);
58 h2 = hex_to_int(hexval[2 * i + 1]);
59 if (h1 == -1 || h2 == -1)
60 return FALSE;
61 hashval[i] = (h1 << 4) | h2;
63 return TRUE;
66 static void hex_encode(char *hexval, const unsigned char *hashval)
68 int i;
70 for (i = 0; i < APR_MD5_DIGESTSIZE; i++)
72 hexval[2 * i] = int_to_hex((hashval[i] >> 4) & 0xf);
73 hexval[2 * i + 1] = int_to_hex(hashval[i] & 0xf);
77 static void compute_digest(unsigned char *digest, const char *challenge,
78 const char *password)
80 unsigned char secret[64];
81 apr_size_t len = strlen(password), i;
82 apr_md5_ctx_t ctx;
84 /* Munge the password into a 64-byte secret. */
85 memset(secret, 0, sizeof(secret));
86 if (len <= sizeof(secret))
87 memcpy(secret, password, len);
88 else
89 apr_md5(secret, password, len);
91 /* Compute MD5(secret XOR opad, MD5(secret XOR ipad, challenge)),
92 * where ipad is a string of 0x36 and opad is a string of 0x5c. */
93 for (i = 0; i < sizeof(secret); i++)
94 secret[i] ^= 0x36;
95 apr_md5_init(&ctx);
96 apr_md5_update(&ctx, secret, sizeof(secret));
97 apr_md5_update(&ctx, challenge, strlen(challenge));
98 apr_md5_final(digest, &ctx);
99 for (i = 0; i < sizeof(secret); i++)
100 secret[i] ^= (0x36 ^ 0x5c);
101 apr_md5_init(&ctx);
102 apr_md5_update(&ctx, secret, sizeof(secret));
103 apr_md5_update(&ctx, digest, APR_MD5_DIGESTSIZE);
104 apr_md5_final(digest, &ctx);
107 /* Fail the authentication, from the server's perspective. */
108 static svn_error_t *fail(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
109 const char *msg)
111 SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure", msg));
112 return svn_ra_svn_flush(conn, pool);
115 /* If we can, make the nonce with random bytes. If we can't... well,
116 * it just has to be different each time. The current time isn't
117 * absolutely guaranteed to be different for each connection, but it
118 * should prevent replay attacks in practice. */
119 static apr_status_t make_nonce(apr_uint64_t *nonce)
121 #if APR_HAS_RANDOM
122 return apr_generate_random_bytes((unsigned char *) nonce, sizeof(*nonce));
123 #else
124 *nonce = apr_time_now();
125 return APR_SUCCESS;
126 #endif
129 svn_error_t *svn_ra_svn_cram_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
130 svn_config_t *pwdb, const char **user,
131 svn_boolean_t *success)
133 apr_status_t status;
134 apr_uint64_t nonce;
135 char hostbuf[APRMAXHOSTLEN + 1];
136 unsigned char cdigest[APR_MD5_DIGESTSIZE], sdigest[APR_MD5_DIGESTSIZE];
137 const char *challenge, *sep, *password;
138 svn_ra_svn_item_t *item;
139 svn_string_t *resp;
141 *success = FALSE;
143 /* Send a challenge. */
144 status = make_nonce(&nonce);
145 if (!status)
146 status = apr_gethostname(hostbuf, sizeof(hostbuf), pool);
147 if (status)
148 return fail(conn, pool, "Internal server error in authentication");
149 challenge = apr_psprintf(pool,
150 "<%" APR_UINT64_T_FMT ".%" APR_TIME_T_FMT "@%s>",
151 nonce, apr_time_now(), hostbuf);
152 SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "step", challenge));
154 /* Read the client's response and decode it into *user and cdigest. */
155 SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
156 if (item->kind != SVN_RA_SVN_STRING) /* Very wrong; don't report failure */
157 return SVN_NO_ERROR;
158 resp = item->u.string;
159 sep = strrchr(resp->data, ' ');
160 if (!sep || resp->len - (sep + 1 - resp->data) != APR_MD5_DIGESTSIZE * 2
161 || !hex_decode(cdigest, sep + 1))
162 return fail(conn, pool, "Malformed client response in authentication");
163 *user = apr_pstrmemdup(pool, resp->data, sep - resp->data);
165 /* Verify the digest against the password in pwfile. */
166 svn_config_get(pwdb, &password, SVN_CONFIG_SECTION_USERS, *user, NULL);
167 if (!password)
168 return fail(conn, pool, "Username not found");
169 compute_digest(sdigest, challenge, password);
170 if (memcmp(cdigest, sdigest, sizeof(sdigest)) != 0)
171 return fail(conn, pool, "Password incorrect");
173 *success = TRUE;
174 return svn_ra_svn_write_tuple(conn, pool, "w()", "success");
177 svn_error_t *svn_ra_svn__cram_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
178 const char *user, const char *password,
179 const char **message)
181 const char *status, *str, *reply;
182 unsigned char digest[APR_MD5_DIGESTSIZE];
183 char hex[2 * APR_MD5_DIGESTSIZE + 1];
185 /* Read the server challenge. */
186 SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &str));
187 if (strcmp(status, "failure") == 0 && str)
189 *message = str;
190 return SVN_NO_ERROR;
192 else if (strcmp(status, "step") != 0 || !str)
193 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
194 _("Unexpected server response to authentication"));
196 /* Write our response. */
197 compute_digest(digest, str, password);
198 hex_encode(hex, digest);
199 hex[sizeof(hex) - 1] = '\0';
200 reply = apr_psprintf(pool, "%s %s", user, hex);
201 SVN_ERR(svn_ra_svn_write_cstring(conn, pool, reply));
203 /* Read the success or failure response from the server. */
204 SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &str));
205 if (strcmp(status, "failure") == 0 && str)
207 *message = str;
208 return SVN_NO_ERROR;
210 else if (strcmp(status, "success") != 0 || str)
211 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
212 _("Unexpected server response to authentication"));
214 *message = NULL;
215 return SVN_NO_ERROR;