2 * cyrus_auth.c : functions for Cyrus SASL-based authentication
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #include "svn_private_config.h"
27 #define APR_WANT_STRFUNC
29 #include <apr_general.h>
30 #include <apr_strings.h>
31 #include <apr_version.h>
33 #include "svn_types.h"
34 #include "svn_string.h"
35 #include "svn_error.h"
36 #include "svn_pools.h"
38 #include "svn_ra_svn.h"
39 #include "svn_base64.h"
41 #include "private/svn_atomic.h"
42 #include "private/ra_svn_sasl.h"
43 #include "private/svn_mutex.h"
47 /* Note: In addition to being used via svn_atomic__init_once to control
48 * initialization of the SASL code this will also be referenced in
49 * the various functions that work with sasl mutexes to determine
50 * if the sasl pool has been destroyed. This should be safe, since
51 * it is only set back to zero in the sasl pool's cleanups, which
52 * only happens during apr_terminate, which we assume is occurring
53 * in atexit processing, at which point we are already running in
54 * single threaded mode.
56 volatile svn_atomic_t svn_ra_svn__sasl_status = 0;
58 /* Initialized by svn_ra_svn__sasl_common_init(). */
59 static volatile svn_atomic_t sasl_ctx_count;
61 static apr_pool_t *sasl_pool = NULL;
64 /* Pool cleanup called when sasl_pool is destroyed. */
65 static apr_status_t sasl_done_cb(void *data)
67 /* Reset svn_ra_svn__sasl_status, in case the client calls
68 apr_initialize()/apr_terminate() more than once. */
69 svn_ra_svn__sasl_status = 0;
70 if (svn_atomic_dec(&sasl_ctx_count) == 0)
76 /* Cyrus SASL is thread-safe only if we supply it with mutex functions
77 * (with sasl_set_mutex()). To make this work with APR, we need to use the
78 * global sasl_pool for the mutex allocations. Freeing a mutex actually
79 * returns it to a global array. We allocate mutexes from this
80 * array if it is non-empty, or directly from the pool otherwise.
81 * We also need a mutex to serialize accesses to the array itself.
84 /* An array of allocated, but unused, apr_thread_mutex_t's. */
85 static apr_array_header_t *free_mutexes = NULL;
87 /* A mutex to serialize access to the array. */
88 static svn_mutex__t *array_mutex = NULL;
90 /* Callbacks we pass to sasl_set_mutex(). */
93 sasl_mutex_alloc_cb_internal(svn_mutex__t **mutex)
95 if (apr_is_empty_array(free_mutexes))
96 return svn_mutex__init(mutex, TRUE, sasl_pool);
98 *mutex = *((svn_mutex__t**)apr_array_pop(free_mutexes));
103 static void *sasl_mutex_alloc_cb(void)
105 svn_mutex__t *mutex = NULL;
108 if (!svn_ra_svn__sasl_status)
111 err = svn_mutex__lock(array_mutex);
113 svn_error_clear(err);
115 svn_error_clear(svn_mutex__unlock(array_mutex,
116 sasl_mutex_alloc_cb_internal(&mutex)));
121 static int check_result(svn_error_t *err)
125 svn_error_clear(err);
132 static int sasl_mutex_lock_cb(void *mutex)
134 if (!svn_ra_svn__sasl_status)
136 return check_result(svn_mutex__lock(mutex));
139 static int sasl_mutex_unlock_cb(void *mutex)
141 if (!svn_ra_svn__sasl_status)
143 return check_result(svn_mutex__unlock(mutex, SVN_NO_ERROR));
147 sasl_mutex_free_cb_internal(void *mutex)
149 APR_ARRAY_PUSH(free_mutexes, svn_mutex__t*) = mutex;
153 static void sasl_mutex_free_cb(void *mutex)
157 if (!svn_ra_svn__sasl_status)
160 err = svn_mutex__lock(array_mutex);
162 svn_error_clear(err);
164 svn_error_clear(svn_mutex__unlock(array_mutex,
165 sasl_mutex_free_cb_internal(mutex)));
167 #endif /* APR_HAS_THREADS */
170 svn_ra_svn__sasl_common_init(apr_pool_t *pool)
172 sasl_pool = svn_pool_create(pool);
174 apr_pool_cleanup_register(sasl_pool, NULL, sasl_done_cb,
175 apr_pool_cleanup_null);
177 sasl_set_mutex(sasl_mutex_alloc_cb,
179 sasl_mutex_unlock_cb,
181 free_mutexes = apr_array_make(sasl_pool, 0, sizeof(svn_mutex__t *));
182 SVN_ERR(svn_mutex__init(&array_mutex, TRUE, sasl_pool));
184 #endif /* APR_HAS_THREADS */
189 /* We are going to look at errno when we get SASL_FAIL but we don't
190 know for sure whether SASL always sets errno. Clearing errno
191 before calling SASL functions helps in cases where SASL does
192 nothing to set errno. */
193 #ifdef apr_set_os_error
194 #define clear_sasl_errno() apr_set_os_error(APR_SUCCESS)
196 #define clear_sasl_errno() (void)0
199 /* Sometimes SASL returns SASL_FAIL as RESULT and sets errno.
200 * SASL_FAIL translates to "generic error" which is quite unhelpful.
201 * Try to append a more informative error message based on errno so
202 * should be called before doing anything that may change errno. */
204 get_sasl_errno_msg(int result, apr_pool_t *result_pool)
206 #ifdef apr_get_os_error
209 if (result == SASL_FAIL && apr_get_os_error() != 0)
210 return apr_psprintf(result_pool, ": %s",
211 svn_strerror(apr_get_os_error(), buf, sizeof(buf)));
216 /* Wrap an error message from SASL with a prefix that allows users
217 * to tell that the error message came from SASL. Queries errno and
218 * so should be called before doing anything that may change errno. */
220 get_sasl_error(sasl_conn_t *sasl_ctx, int result, apr_pool_t *result_pool)
222 const char *sasl_errno_msg = get_sasl_errno_msg(result, result_pool);
224 return apr_psprintf(result_pool,
225 _("SASL authentication error: %s%s"),
226 sasl_errdetail(sasl_ctx), sasl_errno_msg);
229 static svn_error_t *sasl_init_cb(void *baton, apr_pool_t *pool)
233 SVN_ERR(svn_ra_svn__sasl_common_init(pool));
235 result = sasl_client_init(NULL);
236 if (result != SASL_OK)
238 const char *sasl_errno_msg = get_sasl_errno_msg(result, pool);
240 return svn_error_createf
241 (SVN_ERR_RA_NOT_AUTHORIZED, NULL,
242 _("Could not initialized the SASL library: %s%s"),
243 sasl_errstring(result, NULL, NULL),
250 svn_error_t *svn_ra_svn__sasl_init(void)
252 SVN_ERR(svn_atomic__init_once(&svn_ra_svn__sasl_status,
253 sasl_init_cb, NULL, NULL));
257 static apr_status_t sasl_dispose_cb(void *data)
259 sasl_conn_t *sasl_ctx = data;
260 sasl_dispose(&sasl_ctx);
261 if (svn_atomic_dec(&sasl_ctx_count) == 0)
266 void svn_ra_svn__default_secprops(sasl_security_properties_t *secprops)
268 /* The minimum and maximum security strength factors that the chosen
269 SASL mechanism should provide. 0 means 'no encryption', 256 means
270 '256-bit encryption', which is about the best that any SASL
271 mechanism can provide. Using these values effectively means 'use
272 whatever encryption the other side wants'. Note that SASL will try
273 to use better encryption whenever possible, so if both the server and
274 the client use these values the highest possible encryption strength
276 secprops->min_ssf = 0;
277 secprops->max_ssf = 256;
279 /* Set maxbufsize to the maximum amount of data we can read at any one time.
280 This value needs to be commmunicated to the peer if a security layer
282 secprops->maxbufsize = SVN_RA_SVN__READBUF_SIZE;
284 secprops->security_flags = 0;
285 secprops->property_names = secprops->property_values = NULL;
288 /* A baton type used by the SASL username and password callbacks. */
289 typedef struct cred_baton {
290 svn_auth_baton_t *auth_baton;
291 svn_auth_iterstate_t *iterstate;
292 const char *realmstring;
294 /* Unfortunately SASL uses two separate callbacks for the username and
295 password, but we must fetch both of them at the same time. So we cache
296 their values in the baton, set them to NULL individually when SASL
297 demands them, and fetch the next pair when both are NULL. */
298 const char *username;
299 const char *password;
301 /* Any errors we receive from svn_auth_{first,next}_credentials
305 /* This flag is set when we run out of credential providers. */
306 svn_boolean_t no_more_creds;
308 /* Were the auth callbacks ever called? */
309 svn_boolean_t was_used;
314 /* Call svn_auth_{first,next}_credentials. If successful, set BATON->username
315 and BATON->password to the new username and password and return TRUE,
316 otherwise return FALSE. If there are no more credentials, set
317 BATON->no_more_creds to TRUE. Any errors are saved in BATON->err. */
319 get_credentials(cred_baton_t *baton)
323 if (baton->iterstate)
324 baton->err = svn_auth_next_credentials(&creds, baton->iterstate,
327 baton->err = svn_auth_first_credentials(&creds, &baton->iterstate,
328 SVN_AUTH_CRED_SIMPLE,
330 baton->auth_baton, baton->pool);
336 baton->no_more_creds = TRUE;
340 baton->username = ((svn_auth_cred_simple_t *)creds)->username;
341 baton->password = ((svn_auth_cred_simple_t *)creds)->password;
342 baton->was_used = TRUE;
347 /* The username callback. Implements the sasl_getsimple_t interface. */
349 get_username_cb(void *b, int id, const char **username, size_t *len)
351 cred_baton_t *baton = b;
353 if (baton->username || get_credentials(baton))
355 *username = baton->username;
357 *len = strlen(baton->username);
358 baton->username = NULL;
366 /* The password callback. Implements the sasl_getsecret_t interface. */
368 get_password_cb(sasl_conn_t *conn, void *b, int id, sasl_secret_t **psecret)
370 cred_baton_t *baton = b;
372 if (baton->password || get_credentials(baton))
374 sasl_secret_t *secret;
375 size_t len = strlen(baton->password);
377 /* sasl_secret_t is a struct with a variable-sized array as a final
378 member, which means we need to allocate len-1 supplementary bytes
379 (one byte is part of sasl_secret_t, and we don't need a NULL
381 secret = apr_palloc(baton->pool, sizeof(*secret) + len - 1);
383 memcpy(secret->data, baton->password, len);
384 baton->password = NULL;
393 /* Create a new SASL context. */
394 static svn_error_t *new_sasl_ctx(sasl_conn_t **sasl_ctx,
395 svn_boolean_t is_tunneled,
396 const char *hostname,
397 const char *local_addrport,
398 const char *remote_addrport,
399 sasl_callback_t *callbacks,
402 sasl_security_properties_t secprops;
406 result = sasl_client_new(SVN_RA_SVN_SASL_NAME,
407 hostname, local_addrport, remote_addrport,
408 callbacks, SASL_SUCCESS_DATA,
410 if (result != SASL_OK)
412 const char *sasl_errno_msg = get_sasl_errno_msg(result, pool);
414 return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
415 _("Could not create SASL context: %s%s"),
416 sasl_errstring(result, NULL, NULL),
419 svn_atomic_inc(&sasl_ctx_count);
420 apr_pool_cleanup_register(pool, *sasl_ctx, sasl_dispose_cb,
421 apr_pool_cleanup_null);
425 /* We need to tell SASL that this connection is tunneled,
426 otherwise it will ignore EXTERNAL. The third parameter
427 should be the username, but since SASL doesn't seem
428 to use it on the client side, any non-empty string will do. */
430 result = sasl_setprop(*sasl_ctx,
431 SASL_AUTH_EXTERNAL, " ");
432 if (result != SASL_OK)
433 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
434 get_sasl_error(*sasl_ctx, result, pool));
437 /* Set security properties. */
438 svn_ra_svn__default_secprops(&secprops);
439 sasl_setprop(*sasl_ctx, SASL_SEC_PROPS, &secprops);
444 /* Perform an authentication exchange */
445 static svn_error_t *try_auth(svn_ra_svn__session_baton_t *sess,
446 sasl_conn_t *sasl_ctx,
447 svn_boolean_t *success,
448 const char **last_err,
449 const char *mechstring,
452 sasl_interact_t *client_interact = NULL;
453 const char *out, *mech, *status = NULL;
454 const svn_string_t *arg = NULL, *in;
463 result = sasl_client_start(sasl_ctx,
476 return svn_error_create(SVN_ERR_RA_SVN_NO_MECHANISMS, NULL, NULL);
479 /* Fatal error. Fail the authentication. */
480 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
481 get_sasl_error(sasl_ctx, result, pool));
483 /* For anything else, delete the mech from the list
486 const char *pmech = strstr(mechstring, mech);
487 const char *head = apr_pstrndup(pool, mechstring,
489 const char *tail = pmech + strlen(mech);
491 mechstring = apr_pstrcat(pool, head, tail, (char *)NULL);
498 /* Prepare the initial authentication token. */
499 if (outlen > 0 || strcmp(mech, "EXTERNAL") == 0)
500 arg = svn_base64_encode_string2(svn_string_ncreate(out, outlen, pool),
503 /* Send the initial client response */
504 SVN_ERR(svn_ra_svn__auth_response(sess->conn, pool, mech,
505 arg ? arg->data : NULL));
507 while (result == SASL_CONTINUE)
509 /* Read the server response */
510 SVN_ERR(svn_ra_svn__read_tuple(sess->conn, pool, "w(?s)",
513 if (strcmp(status, "failure") == 0)
515 /* Authentication failed. Use the next set of credentials */
517 /* Remember the message sent by the server because we'll want to
518 return a meaningful error if we run out of auth providers. */
519 *last_err = in ? in->data : "";
523 if ((strcmp(status, "success") != 0 && strcmp(status, "step") != 0)
525 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
526 _("Unexpected server response"
527 " to authentication"));
529 /* If the mech is CRAM-MD5 we don't base64-decode the server response. */
530 if (strcmp(mech, "CRAM-MD5") != 0)
531 in = svn_base64_decode_string(in, pool);
534 result = sasl_client_step(sasl_ctx,
536 (const unsigned int) in->len,
538 &out, /* Filled in by SASL. */
541 if (result != SASL_OK && result != SASL_CONTINUE)
542 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
543 get_sasl_error(sasl_ctx, result, pool));
545 /* If the server thinks we're done, then don't send any response. */
546 if (strcmp(status, "success") == 0)
551 arg = svn_string_ncreate(out, outlen, pool);
552 /* Write our response. */
553 /* For CRAM-MD5, we don't use base64-encoding. */
554 if (strcmp(mech, "CRAM-MD5") != 0)
555 arg = svn_base64_encode_string2(arg, TRUE, pool);
556 SVN_ERR(svn_ra_svn__write_cstring(sess->conn, pool, arg->data));
560 SVN_ERR(svn_ra_svn__write_cstring(sess->conn, pool, ""));
564 if (!status || strcmp(status, "step") == 0)
566 /* This is a client-send-last mech. Read the last server response. */
567 SVN_ERR(svn_ra_svn__read_tuple(sess->conn, pool, "w(?s)",
570 if (strcmp(status, "failure") == 0)
573 *last_err = in ? in->data : "";
575 else if (strcmp(status, "success") == 0)
581 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
582 _("Unexpected server response"
583 " to authentication"));
590 /* Baton for a SASL encrypted svn_ra_svn__stream_t. */
591 typedef struct sasl_baton {
592 svn_ra_svn__stream_t *stream; /* Inherited stream. */
593 sasl_conn_t *ctx; /* The SASL context for this connection. */
594 unsigned int maxsize; /* The maximum amount of data we can encode. */
595 const char *read_buf; /* The buffer returned by sasl_decode. */
596 unsigned int read_len; /* Its current length. */
597 const char *write_buf; /* The buffer returned by sasl_encode. */
598 unsigned int write_len; /* Its length. */
599 apr_pool_t *scratch_pool;
602 /* Functions to implement a SASL encrypted svn_ra_svn__stream_t. */
604 /* Implements svn_read_fn_t. */
605 static svn_error_t *sasl_read_cb(void *baton, char *buffer, apr_size_t *len)
607 sasl_baton_t *sasl_baton = baton;
609 /* A copy of *len, used by the wrapped stream. */
610 apr_size_t len2 = *len;
612 /* sasl_decode might need more data than a single read can provide,
613 hence the need to put a loop around the decoding. */
614 while (! sasl_baton->read_buf || sasl_baton->read_len == 0)
616 SVN_ERR(svn_ra_svn__stream_read(sasl_baton->stream, buffer, &len2));
623 result = sasl_decode(sasl_baton->ctx, buffer, (unsigned int) len2,
624 &sasl_baton->read_buf,
625 &sasl_baton->read_len);
626 if (result != SASL_OK)
627 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
628 get_sasl_error(sasl_baton->ctx, result,
629 sasl_baton->scratch_pool));
632 /* The buffer returned by sasl_decode might be larger than what the
633 caller wants. If this is the case, we only copy back *len bytes now
634 (the rest will be returned by subsequent calls to this function).
635 If not, we just copy back the whole thing. */
636 if (*len >= sasl_baton->read_len)
638 memcpy(buffer, sasl_baton->read_buf, sasl_baton->read_len);
639 *len = sasl_baton->read_len;
640 sasl_baton->read_buf = NULL;
641 sasl_baton->read_len = 0;
645 memcpy(buffer, sasl_baton->read_buf, *len);
646 sasl_baton->read_len -= *len;
647 sasl_baton->read_buf += *len;
653 /* Implements svn_write_fn_t. */
655 sasl_write_cb(void *baton, const char *buffer, apr_size_t *len)
657 sasl_baton_t *sasl_baton = baton;
660 if (! sasl_baton->write_buf || sasl_baton->write_len == 0)
662 /* Make sure we don't write too much. */
663 *len = (*len > sasl_baton->maxsize) ? sasl_baton->maxsize : *len;
665 result = sasl_encode(sasl_baton->ctx, buffer, (unsigned int) *len,
666 &sasl_baton->write_buf,
667 &sasl_baton->write_len);
669 if (result != SASL_OK)
670 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
671 get_sasl_error(sasl_baton->ctx, result,
672 sasl_baton->scratch_pool));
677 apr_size_t tmplen = sasl_baton->write_len;
678 SVN_ERR(svn_ra_svn__stream_write(sasl_baton->stream,
679 sasl_baton->write_buf,
683 /* The output buffer and its length will be preserved in sasl_baton
684 and will be written out during the next call to this function
685 (which will have the same arguments). */
689 sasl_baton->write_len -= (unsigned int) tmplen;
690 sasl_baton->write_buf += tmplen;
692 while (sasl_baton->write_len > 0);
694 sasl_baton->write_buf = NULL;
695 sasl_baton->write_len = 0;
700 /* Implements ra_svn_timeout_fn_t. */
701 static void sasl_timeout_cb(void *baton, apr_interval_time_t interval)
703 sasl_baton_t *sasl_baton = baton;
704 svn_ra_svn__stream_timeout(sasl_baton->stream, interval);
707 /* Implements ra_svn_pending_fn_t. */
708 static svn_boolean_t sasl_pending_cb(void *baton)
710 sasl_baton_t *sasl_baton = baton;
711 return svn_ra_svn__stream_pending(sasl_baton->stream);
714 svn_error_t *svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t *conn,
715 sasl_conn_t *sasl_ctx,
718 const sasl_ssf_t *ssfp;
720 if (! conn->encrypted)
724 /* Get the strength of the security layer. */
726 result = sasl_getprop(sasl_ctx, SASL_SSF, (void*) &ssfp);
727 if (result != SASL_OK)
728 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
729 get_sasl_error(sasl_ctx, result, pool));
733 sasl_baton_t *sasl_baton;
736 /* Flush the connection, as we're about to replace its stream. */
737 SVN_ERR(svn_ra_svn__flush(conn, pool));
739 /* Create and initialize the stream baton. */
740 sasl_baton = apr_pcalloc(conn->pool, sizeof(*sasl_baton));
741 sasl_baton->ctx = sasl_ctx;
742 sasl_baton->scratch_pool = conn->pool;
744 /* Find out the maximum input size for sasl_encode. */
746 result = sasl_getprop(sasl_ctx, SASL_MAXOUTBUF, &maxsize);
747 if (result != SASL_OK)
748 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
749 get_sasl_error(sasl_ctx, result, pool));
750 sasl_baton->maxsize = *((const unsigned int *) maxsize);
752 /* If there is any data left in the read buffer at this point,
753 we need to decrypt it. */
754 if (conn->read_end > conn->read_ptr)
757 result = sasl_decode(sasl_ctx, conn->read_ptr,
758 (unsigned int) (conn->read_end - conn->read_ptr),
759 &sasl_baton->read_buf, &sasl_baton->read_len);
760 if (result != SASL_OK)
761 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
762 get_sasl_error(sasl_ctx, result, pool));
763 conn->read_end = conn->read_ptr;
766 /* Wrap the existing stream. */
767 sasl_baton->stream = conn->stream;
769 conn->stream = svn_ra_svn__stream_create(sasl_baton, sasl_read_cb,
772 sasl_pending_cb, conn->pool);
773 /* Yay, we have a security layer! */
774 conn->encrypted = TRUE;
780 svn_error_t *svn_ra_svn__get_addresses(const char **local_addrport,
781 const char **remote_addrport,
782 svn_ra_svn_conn_t *conn,
787 apr_status_t apr_err;
788 apr_sockaddr_t *local_sa, *remote_sa;
789 char *local_addr, *remote_addr;
791 apr_err = apr_socket_addr_get(&local_sa, APR_LOCAL, conn->sock);
793 return svn_error_wrap_apr(apr_err, NULL);
795 apr_err = apr_socket_addr_get(&remote_sa, APR_REMOTE, conn->sock);
797 return svn_error_wrap_apr(apr_err, NULL);
799 apr_err = apr_sockaddr_ip_get(&local_addr, local_sa);
801 return svn_error_wrap_apr(apr_err, NULL);
803 apr_err = apr_sockaddr_ip_get(&remote_addr, remote_sa);
805 return svn_error_wrap_apr(apr_err, NULL);
807 /* Format the IP address and port number like this: a.b.c.d;port */
808 *local_addrport = apr_pstrcat(pool, local_addr, ";",
809 apr_itoa(pool, (int)local_sa->port),
811 *remote_addrport = apr_pstrcat(pool, remote_addr, ";",
812 apr_itoa(pool, (int)remote_sa->port),
819 svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess,
820 const apr_array_header_t *mechlist,
821 const char *realm, apr_pool_t *pool)
824 sasl_conn_t *sasl_ctx;
825 const char *mechstring = "", *last_err = "", *realmstring;
826 const char *local_addrport = NULL, *remote_addrport = NULL;
827 svn_boolean_t success;
828 sasl_callback_t *callbacks;
829 cred_baton_t cred_baton = { 0 };
832 if (!sess->is_tunneled)
834 SVN_ERR(svn_ra_svn__get_addresses(&local_addrport, &remote_addrport,
838 /* Prefer EXTERNAL, then ANONYMOUS, then let SASL decide. */
839 if (svn_ra_svn__find_mech(mechlist, "EXTERNAL"))
840 mechstring = "EXTERNAL";
841 else if (svn_ra_svn__find_mech(mechlist, "ANONYMOUS"))
842 mechstring = "ANONYMOUS";
845 /* Create a string containing the list of mechanisms, separated by spaces. */
846 for (i = 0; i < mechlist->nelts; i++)
848 svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(mechlist, i, svn_ra_svn_item_t);
849 mechstring = apr_pstrcat(pool,
852 elt->u.word, (char *)NULL);
856 realmstring = apr_psprintf(pool, "%s %s", sess->realm_prefix, realm);
858 /* Initialize the credential baton. */
859 cred_baton.auth_baton = sess->callbacks->auth_baton;
860 cred_baton.realmstring = realmstring;
861 cred_baton.pool = pool;
863 /* Reserve space for 3 callbacks (for the username, password and the
864 array terminator). These structures must persist until the
865 disposal of the SASL context at pool cleanup, however the
866 callback functions will not be invoked outside this function so
867 other structures can have a shorter lifetime. */
868 callbacks = apr_palloc(sess->conn->pool, sizeof(*callbacks) * 3);
870 /* Initialize the callbacks array. */
872 /* The username callback. */
873 callbacks[0].id = SASL_CB_AUTHNAME;
874 callbacks[0].proc = (int (*)(void))get_username_cb;
875 callbacks[0].context = &cred_baton;
877 /* The password callback. */
878 callbacks[1].id = SASL_CB_PASS;
879 callbacks[1].proc = (int (*)(void))get_password_cb;
880 callbacks[1].context = &cred_baton;
882 /* Mark the end of the array. */
883 callbacks[2].id = SASL_CB_LIST_END;
884 callbacks[2].proc = NULL;
885 callbacks[2].context = NULL;
887 subpool = svn_pool_create(pool);
892 /* If last_err was set to a non-empty string, it needs to be duplicated
893 to the parent pool before the subpool is cleared. */
895 last_err = apr_pstrdup(pool, last_err);
896 svn_pool_clear(subpool);
898 SVN_ERR(new_sasl_ctx(&sasl_ctx, sess->is_tunneled,
899 sess->hostname, local_addrport, remote_addrport,
900 callbacks, sess->conn->pool));
901 err = try_auth(sess, sasl_ctx, &success, &last_err, mechstring,
904 /* If we encountered an error while fetching credentials, that error
908 svn_error_clear(err);
909 return cred_baton.err;
911 if (cred_baton.no_more_creds
912 || (! err && ! success && ! cred_baton.was_used))
914 svn_error_clear(err);
915 /* If we ran out of authentication providers, or if we got a server
916 error and our callbacks were never called, there's no point in
917 retrying authentication. Return the last error sent by the
920 return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
921 _("Authentication error from server: %s"),
923 /* Hmm, we don't have a server error. Return a generic error. */
924 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
925 _("Can't get username or password"));
929 if (err->apr_err == SVN_ERR_RA_SVN_NO_MECHANISMS)
931 svn_error_clear(err);
933 /* We could not find a supported mechanism in the list sent by the
934 server. In many cases this happens because the client is missing
935 the CRAM-MD5 or ANONYMOUS plugins, in which case we can simply use
936 the built-in implementation. In all other cases this call will be
937 useless, but hey, at least we'll get consistent error messages. */
938 return svn_ra_svn__do_internal_auth(sess, mechlist,
945 svn_pool_destroy(subpool);
947 SVN_ERR(svn_ra_svn__enable_sasl_encryption(sess->conn, sasl_ctx, pool));
949 SVN_ERR(svn_auth_save_credentials(cred_baton.iterstate, pool));
954 #endif /* SVN_HAVE_SASL */