2 * cyrus_auth.c : functions for Cyrus SASL-based authentication
4 * ====================================================================
5 * Copyright (c) 2006-2007 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 * ====================================================================
19 #include "svn_private_config.h"
22 #define APR_WANT_STRFUNC
24 #include <apr_general.h>
25 #include <apr_strings.h>
26 #include <apr_atomic.h>
27 #include <apr_thread_mutex.h>
28 #include <apr_version.h>
30 #include "svn_types.h"
31 #include "svn_string.h"
32 #include "svn_error.h"
33 #include "svn_pools.h"
35 #include "svn_ra_svn.h"
36 #include "svn_base64.h"
38 #include "private/svn_atomic.h"
39 #include "private/ra_svn_sasl.h"
43 /* Note: In addition to being used via svn_atomic__init_once to control
44 * initialization of the SASL code this will also be referenced in
45 * the various functions that work with sasl mutexes to determine
46 * if the sasl pool has been destroyed. This should be safe, since
47 * it is only set back to zero in the sasl pool's cleanups, which
48 * only happens during apr_terminate, which we assume is occurring
49 * in atexit processing, at which point we are already running in
50 * single threaded mode.
52 volatile svn_atomic_t svn_ra_svn__sasl_status
;
54 static volatile svn_atomic_t sasl_ctx_count
;
56 static apr_pool_t
*sasl_pool
= NULL
;
59 /* Pool cleanup called when sasl_pool is destroyed. */
60 static apr_status_t
sasl_done_cb(void *data
)
62 /* Reset svn_ra_svn__sasl_status, in case the client calls
63 apr_initialize()/apr_terminate() more than once. */
64 svn_ra_svn__sasl_status
= 0;
65 if (svn_atomic_dec(&sasl_ctx_count
) == 0)
71 /* Cyrus SASL is thread-safe only if we supply it with mutex functions
72 * (with sasl_set_mutex()). To make this work with APR, we need to use the
73 * global sasl_pool for the mutex allocations. Freeing a mutex actually
74 * returns it to a global array. We allocate mutexes from this
75 * array if it is non-empty, or directly from the pool otherwise.
76 * We also need a mutex to serialize accesses to the array itself.
79 /* An array of allocated, but unused, apr_thread_mutex_t's. */
80 static apr_array_header_t
*free_mutexes
= NULL
;
82 /* A mutex to serialize access to the array. */
83 static apr_thread_mutex_t
*array_mutex
= NULL
;
85 /* Callbacks we pass to sasl_set_mutex(). */
87 static void *sasl_mutex_alloc_cb(void)
89 apr_thread_mutex_t
*mutex
;
92 if (!svn_ra_svn__sasl_status
)
95 apr_err
= apr_thread_mutex_lock(array_mutex
);
96 if (apr_err
!= APR_SUCCESS
)
99 if (apr_is_empty_array(free_mutexes
))
101 apr_err
= apr_thread_mutex_create(&mutex
,
102 APR_THREAD_MUTEX_DEFAULT
,
104 if (apr_err
!= APR_SUCCESS
)
108 mutex
= *((apr_thread_mutex_t
**)apr_array_pop(free_mutexes
));
110 apr_err
= apr_thread_mutex_unlock(array_mutex
);
111 if (apr_err
!= APR_SUCCESS
)
117 static int sasl_mutex_lock_cb(void *mutex
)
119 if (!svn_ra_svn__sasl_status
)
121 return (apr_thread_mutex_lock(mutex
) == APR_SUCCESS
) ? 0 : -1;
124 static int sasl_mutex_unlock_cb(void *mutex
)
126 if (!svn_ra_svn__sasl_status
)
128 return (apr_thread_mutex_unlock(mutex
) == APR_SUCCESS
) ? 0 : -1;
131 static void sasl_mutex_free_cb(void *mutex
)
133 if (svn_ra_svn__sasl_status
)
135 apr_status_t apr_err
= apr_thread_mutex_lock(array_mutex
);
136 if (apr_err
== APR_SUCCESS
)
138 APR_ARRAY_PUSH(free_mutexes
, apr_thread_mutex_t
*) = mutex
;
139 apr_thread_mutex_unlock(array_mutex
);
143 #endif /* APR_HAS_THREADS */
145 apr_status_t
svn_ra_svn__sasl_common_init(apr_pool_t
*pool
)
147 apr_status_t apr_err
= APR_SUCCESS
;
149 sasl_pool
= svn_pool_create(pool
);
151 apr_pool_cleanup_register(sasl_pool
, NULL
, sasl_done_cb
,
152 apr_pool_cleanup_null
);
154 sasl_set_mutex(sasl_mutex_alloc_cb
,
156 sasl_mutex_unlock_cb
,
158 free_mutexes
= apr_array_make(sasl_pool
, 0, sizeof(apr_thread_mutex_t
*));
159 apr_err
= apr_thread_mutex_create(&array_mutex
,
160 APR_THREAD_MUTEX_DEFAULT
,
162 #endif /* APR_HAS_THREADS */
166 static svn_error_t
*sasl_init_cb(apr_pool_t
*pool
)
168 if (svn_ra_svn__sasl_common_init(pool
) != APR_SUCCESS
169 || sasl_client_init(NULL
) != SASL_OK
)
170 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
171 _("Could not initialize the SASL library"));
175 svn_error_t
*svn_ra_svn__sasl_init(void)
177 SVN_ERR(svn_atomic__init_once(&svn_ra_svn__sasl_status
, sasl_init_cb
, NULL
));
181 static apr_status_t
sasl_dispose_cb(void *data
)
183 sasl_conn_t
*sasl_ctx
= data
;
184 sasl_dispose(&sasl_ctx
);
185 if (svn_atomic_dec(&sasl_ctx_count
) == 0)
190 void svn_ra_svn__default_secprops(sasl_security_properties_t
*secprops
)
192 /* The minimum and maximum security strength factors that the chosen
193 SASL mechanism should provide. 0 means 'no encryption', 256 means
194 '256-bit encryption', which is about the best that any SASL
195 mechanism can provide. Using these values effectively means 'use
196 whatever encryption the other side wants'. Note that SASL will try
197 to use better encryption whenever possible, so if both the server and
198 the client use these values the highest possible encryption strength
200 secprops
->min_ssf
= 0;
201 secprops
->max_ssf
= 256;
203 /* Set maxbufsize to the maximum amount of data we can read at any one time.
204 This value needs to be commmunicated to the peer if a security layer
206 secprops
->maxbufsize
= SVN_RA_SVN__READBUF_SIZE
;
208 secprops
->security_flags
= 0;
209 secprops
->property_names
= secprops
->property_values
= NULL
;
212 /* A baton type used by the SASL username and password callbacks. */
213 typedef struct cred_baton
{
214 svn_auth_baton_t
*auth_baton
;
215 svn_auth_iterstate_t
*iterstate
;
216 const char *realmstring
;
218 /* Unfortunately SASL uses two separate callbacks for the username and
219 password, but we must fetch both of them at the same time. So we cache
220 their values in the baton, set them to NULL individually when SASL
221 demands them, and fetch the next pair when both are NULL. */
222 const char *username
;
223 const char *password
;
225 /* Any errors we receive from svn_auth_{first,next}_credentials
229 /* This flag is set when we run out of credential providers. */
230 svn_boolean_t no_more_creds
;
232 /* Were the auth callbacks ever called? */
233 svn_boolean_t was_used
;
238 /* Call svn_auth_{first,next}_credentials. If successful, set BATON->username
239 and BATON->password to the new username and password and return TRUE,
240 otherwise return FALSE. If there are no more credentials, set
241 BATON->no_more_creds to TRUE. Any errors are saved in BATON->err. */
243 get_credentials(cred_baton_t
*baton
)
247 if (baton
->iterstate
)
248 baton
->err
= svn_auth_next_credentials(&creds
, baton
->iterstate
,
251 baton
->err
= svn_auth_first_credentials(&creds
, &baton
->iterstate
,
252 SVN_AUTH_CRED_SIMPLE
,
254 baton
->auth_baton
, baton
->pool
);
260 baton
->no_more_creds
= TRUE
;
264 baton
->username
= ((svn_auth_cred_simple_t
*)creds
)->username
;
265 baton
->password
= ((svn_auth_cred_simple_t
*)creds
)->password
;
266 baton
->was_used
= TRUE
;
271 /* The username callback. Implements the sasl_getsimple_t interface. */
273 get_username_cb(void *b
, int id
, const char **username
, unsigned *len
)
275 cred_baton_t
*baton
= b
;
277 if (baton
->username
|| get_credentials(baton
))
279 *username
= baton
->username
;
281 *len
= strlen(baton
->username
);
282 baton
->username
= NULL
;
290 /* The password callback. Implements the sasl_getsecret_t interface. */
292 get_password_cb(sasl_conn_t
*conn
, void *b
, int id
, sasl_secret_t
**psecret
)
294 cred_baton_t
*baton
= b
;
296 if (baton
->password
|| get_credentials(baton
))
298 sasl_secret_t
*secret
;
299 int len
= strlen(baton
->password
);
301 /* sasl_secret_t is a struct with a variable-sized array as a final
302 member, which means we need to allocate len-1 supplementary bytes
303 (one byte is part of sasl_secret_t, and we don't need a NULL
305 secret
= apr_palloc(baton
->pool
, sizeof(*secret
) + len
- 1);
307 memcpy(secret
->data
, baton
->password
, len
);
308 baton
->password
= NULL
;
317 /* Create a new SASL context. */
318 static svn_error_t
*new_sasl_ctx(sasl_conn_t
**sasl_ctx
,
319 svn_boolean_t is_tunneled
,
320 const char *hostname
,
321 const char *local_addrport
,
322 const char *remote_addrport
,
323 sasl_callback_t
*callbacks
,
326 sasl_security_properties_t secprops
;
329 result
= sasl_client_new("svn", hostname
, local_addrport
, remote_addrport
,
330 callbacks
, SASL_SUCCESS_DATA
,
332 if (result
!= SASL_OK
)
333 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
334 sasl_errstring(result
, NULL
, NULL
));
336 svn_atomic_inc(&sasl_ctx_count
);
337 apr_pool_cleanup_register(pool
, *sasl_ctx
, sasl_dispose_cb
,
338 apr_pool_cleanup_null
);
342 /* We need to tell SASL that this connection is tunneled,
343 otherwise it will ignore EXTERNAL. The third paramater
344 should be the username, but since SASL doesn't seem
345 to use it on the client side, any non-empty string will do. */
346 result
= sasl_setprop(*sasl_ctx
,
347 SASL_AUTH_EXTERNAL
, " ");
348 if (result
!= SASL_OK
)
349 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
350 sasl_errdetail(*sasl_ctx
));
353 /* Set security properties. Don't allow PLAIN or LOGIN, since we
354 don't support TLS yet. */
355 svn_ra_svn__default_secprops(&secprops
);
356 secprops
.security_flags
= SASL_SEC_NOPLAINTEXT
;
357 sasl_setprop(*sasl_ctx
, SASL_SEC_PROPS
, &secprops
);
362 /* Perform an authentication exchange */
363 static svn_error_t
*try_auth(svn_ra_svn__session_baton_t
*sess
,
364 sasl_conn_t
*sasl_ctx
,
365 svn_boolean_t
*success
,
366 const char **last_err
,
367 const char *mechstring
,
370 sasl_interact_t
*client_interact
= NULL
;
371 const char *out
, *mech
, *status
= NULL
;
372 const svn_string_t
*arg
= NULL
, *in
;
380 result
= sasl_client_start(sasl_ctx
,
393 return svn_error_create(SVN_ERR_RA_SVN_NO_MECHANISMS
, NULL
, NULL
);
396 /* Fatal error. Fail the authentication. */
397 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
398 sasl_errdetail(sasl_ctx
));
400 /* For anything else, delete the mech from the list
403 char *dst
= strstr(mechstring
, mech
);
404 char *src
= dst
+ strlen(mech
);
405 while ((*dst
++ = *src
++) != '\0')
413 /* Prepare the initial authentication token. */
414 if (outlen
> 0 || strcmp(mech
, "EXTERNAL") == 0)
415 arg
= svn_base64_encode_string(svn_string_ncreate(out
, outlen
, pool
),
418 /* Send the initial client response */
419 SVN_ERR(svn_ra_svn__auth_response(sess
->conn
, pool
, mech
,
420 arg
? arg
->data
: NULL
));
422 while (result
== SASL_CONTINUE
)
424 /* Read the server response */
425 SVN_ERR(svn_ra_svn_read_tuple(sess
->conn
, pool
, "w(?s)",
428 if (strcmp(status
, "failure") == 0)
430 /* Authentication failed. Use the next set of credentials */
432 /* Remember the message sent by the server because we'll want to
433 return a meaningful error if we run out of auth providers. */
434 *last_err
= in
? in
->data
: "";
438 if ((strcmp(status
, "success") != 0 && strcmp(status
, "step") != 0)
440 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
441 _("Unexpected server response"
442 " to authentication"));
444 /* If the mech is CRAM-MD5 we don't base64-decode the server response. */
445 if (strcmp(mech
, "CRAM-MD5") != 0)
446 in
= svn_base64_decode_string(in
, pool
);
448 result
= sasl_client_step(sasl_ctx
,
452 &out
, /* Filled in by SASL. */
455 if (result
!= SASL_OK
&& result
!= SASL_CONTINUE
)
456 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
457 sasl_errdetail(sasl_ctx
));
461 arg
= svn_string_ncreate(out
, outlen
, pool
);
462 /* Write our response. */
463 /* For CRAM-MD5, we don't use base64-encoding. */
464 if (strcmp(mech
, "CRAM-MD5") != 0)
465 arg
= svn_base64_encode_string(arg
, pool
);
466 SVN_ERR(svn_ra_svn_write_cstring(sess
->conn
, pool
, arg
->data
));
470 if (!status
|| strcmp(status
, "step") == 0)
472 /* This is a client-send-last mech. Read the last server response. */
473 SVN_ERR(svn_ra_svn_read_tuple(sess
->conn
, pool
, "w(?s)",
476 if (strcmp(status
, "failure") == 0)
479 *last_err
= in
? in
->data
: "";
481 else if (strcmp(status
, "success") == 0)
487 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
488 _("Unexpected server response"
489 " to authentication"));
496 /* Baton for a SASL encrypted svn_ra_svn__stream_t. */
497 typedef struct sasl_baton
{
498 svn_ra_svn__stream_t
*stream
; /* Inherited stream. */
499 sasl_conn_t
*ctx
; /* The SASL context for this connection. */
500 unsigned int maxsize
; /* The maximum amount of data we can encode. */
501 const char *read_buf
; /* The buffer returned by sasl_decode. */
502 unsigned int read_len
; /* Its current length. */
503 const char *write_buf
; /* The buffer returned by sasl_encode. */
504 unsigned int write_len
; /* Its length. */
507 /* Functions to implement a SASL encrypted svn_ra_svn__stream_t. */
509 /* Implements svn_read_fn_t. */
510 static svn_error_t
*sasl_read_cb(void *baton
, char *buffer
, apr_size_t
*len
)
512 sasl_baton_t
*sasl_baton
= baton
;
514 /* A copy of *len, used by the wrapped stream. */
515 apr_size_t len2
= *len
;
517 /* sasl_decode might need more data than a single read can provide,
518 hence the need to put a loop around the decoding. */
519 while (! sasl_baton
->read_buf
|| sasl_baton
->read_len
== 0)
521 SVN_ERR(svn_ra_svn__stream_read(sasl_baton
->stream
, buffer
, &len2
));
527 result
= sasl_decode(sasl_baton
->ctx
, buffer
, len2
,
528 &sasl_baton
->read_buf
,
529 &sasl_baton
->read_len
);
530 if (result
!= SASL_OK
)
531 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
532 sasl_errdetail(sasl_baton
->ctx
));
535 /* The buffer returned by sasl_decode might be larger than what the
536 caller wants. If this is the case, we only copy back *len bytes now
537 (the rest will be returned by subsequent calls to this function).
538 If not, we just copy back the whole thing. */
539 if (*len
>= sasl_baton
->read_len
)
541 memcpy(buffer
, sasl_baton
->read_buf
, sasl_baton
->read_len
);
542 *len
= sasl_baton
->read_len
;
543 sasl_baton
->read_buf
= NULL
;
544 sasl_baton
->read_len
= 0;
548 memcpy(buffer
, sasl_baton
->read_buf
, *len
);
549 sasl_baton
->read_len
-= *len
;
550 sasl_baton
->read_buf
+= *len
;
556 /* Implements svn_write_fn_t. */
558 sasl_write_cb(void *baton
, const char *buffer
, apr_size_t
*len
)
560 sasl_baton_t
*sasl_baton
= baton
;
563 if (! sasl_baton
->write_buf
|| sasl_baton
->write_len
== 0)
565 /* Make sure we don't write too much. */
566 *len
= (*len
> sasl_baton
->maxsize
) ? sasl_baton
->maxsize
: *len
;
567 result
= sasl_encode(sasl_baton
->ctx
, buffer
, *len
,
568 &sasl_baton
->write_buf
,
569 &sasl_baton
->write_len
);
571 if (result
!= SASL_OK
)
572 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
573 sasl_errdetail(sasl_baton
->ctx
));
578 apr_size_t tmplen
= sasl_baton
->write_len
;
579 SVN_ERR(svn_ra_svn__stream_write(sasl_baton
->stream
,
580 sasl_baton
->write_buf
,
584 /* The output buffer and its length will be preserved in sasl_baton
585 and will be written out during the next call to this function
586 (which will have the same arguments). */
590 sasl_baton
->write_len
-= tmplen
;
591 sasl_baton
->write_buf
+= tmplen
;
593 while (sasl_baton
->write_len
> 0);
595 sasl_baton
->write_buf
= NULL
;
596 sasl_baton
->write_len
= 0;
601 /* Implements ra_svn_timeout_fn_t. */
602 static void sasl_timeout_cb(void *baton
, apr_interval_time_t interval
)
604 sasl_baton_t
*sasl_baton
= baton
;
605 svn_ra_svn__stream_timeout(sasl_baton
->stream
, interval
);
608 /* Implements ra_svn_pending_fn_t. */
609 static svn_boolean_t
sasl_pending_cb(void *baton
)
611 sasl_baton_t
*sasl_baton
= baton
;
612 return svn_ra_svn__stream_pending(sasl_baton
->stream
);
615 svn_error_t
*svn_ra_svn__enable_sasl_encryption(svn_ra_svn_conn_t
*conn
,
616 sasl_conn_t
*sasl_ctx
,
619 sasl_baton_t
*sasl_baton
;
620 const sasl_ssf_t
*ssfp
;
624 if (! conn
->encrypted
)
626 /* Get the strength of the security layer. */
627 result
= sasl_getprop(sasl_ctx
, SASL_SSF
, (void*) &ssfp
);
628 if (result
!= SASL_OK
)
629 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
630 sasl_errdetail(sasl_ctx
));
634 /* Flush the connection, as we're about to replace its stream. */
635 SVN_ERR(svn_ra_svn_flush(conn
, pool
));
637 /* Create and initialize the stream baton. */
638 sasl_baton
= apr_pcalloc(conn
->pool
, sizeof(*sasl_baton
));
639 sasl_baton
->ctx
= sasl_ctx
;
641 /* Find out the maximum input size for sasl_encode. */
642 result
= sasl_getprop(sasl_ctx
, SASL_MAXOUTBUF
, &maxsize
);
643 if (result
!= SASL_OK
)
644 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
645 sasl_errdetail(sasl_ctx
));
646 sasl_baton
->maxsize
= *((unsigned int *) maxsize
);
648 /* If there is any data left in the read buffer at this point,
649 we need to decrypt it. */
650 if (conn
->read_end
> conn
->read_ptr
)
652 result
= sasl_decode(sasl_ctx
, conn
->read_ptr
,
653 conn
->read_end
- conn
->read_ptr
,
654 &sasl_baton
->read_buf
,
655 &sasl_baton
->read_len
);
656 if (result
!= SASL_OK
)
657 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
658 sasl_errdetail(sasl_ctx
));
659 conn
->read_end
= conn
->read_ptr
;
662 /* Wrap the existing stream. */
663 sasl_baton
->stream
= conn
->stream
;
665 conn
->stream
= svn_ra_svn__stream_create(sasl_baton
, sasl_read_cb
,
668 sasl_pending_cb
, conn
->pool
);
669 /* Yay, we have a security layer! */
670 conn
->encrypted
= TRUE
;
676 svn_error_t
*svn_ra_svn__get_addresses(const char **local_addrport
,
677 const char **remote_addrport
,
678 svn_ra_svn_conn_t
*conn
,
683 apr_status_t apr_err
;
684 apr_sockaddr_t
*local_sa
, *remote_sa
;
685 char *local_addr
, *remote_addr
;
687 apr_err
= apr_socket_addr_get(&local_sa
, APR_LOCAL
, conn
->sock
);
689 return svn_error_wrap_apr(apr_err
, NULL
);
691 apr_err
= apr_socket_addr_get(&remote_sa
, APR_REMOTE
, conn
->sock
);
693 return svn_error_wrap_apr(apr_err
, NULL
);
695 apr_err
= apr_sockaddr_ip_get(&local_addr
, local_sa
);
697 return svn_error_wrap_apr(apr_err
, NULL
);
699 apr_err
= apr_sockaddr_ip_get(&remote_addr
, remote_sa
);
701 return svn_error_wrap_apr(apr_err
, NULL
);
703 /* Format the IP address and port number like this: a.b.c.d;port */
704 *local_addrport
= apr_pstrcat(pool
, local_addr
, ";",
705 apr_itoa(pool
, (int)local_sa
->port
), NULL
);
706 *remote_addrport
= apr_pstrcat(pool
, remote_addr
, ";",
707 apr_itoa(pool
, (int)remote_sa
->port
), NULL
);
713 svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t
*sess
,
714 apr_array_header_t
*mechlist
,
715 const char *realm
, apr_pool_t
*pool
)
718 sasl_conn_t
*sasl_ctx
;
719 const char *mechstring
= "", *last_err
= "", *realmstring
;
720 const char *local_addrport
= NULL
, *remote_addrport
= NULL
;
721 svn_boolean_t success
;
722 /* Reserve space for 3 callbacks (for the username, password and the
723 array terminator). */
724 sasl_callback_t callbacks
[3];
725 cred_baton_t cred_baton
;
728 if (!sess
->is_tunneled
)
730 SVN_ERR(svn_ra_svn__get_addresses(&local_addrport
, &remote_addrport
,
734 /* Create a string containing the list of mechanisms, separated by spaces. */
735 for (i
= 0; i
< mechlist
->nelts
; i
++)
737 svn_ra_svn_item_t
*elt
= &APR_ARRAY_IDX(mechlist
, i
, svn_ra_svn_item_t
);
739 /* Force the client to use ANONYMOUS or EXTERNAL if they are available.*/
740 if (strcmp(elt
->u
.word
, "ANONYMOUS") == 0
741 || strcmp(elt
->u
.word
, "EXTERNAL") == 0)
743 mechstring
= elt
->u
.word
;
746 mechstring
= apr_pstrcat(pool
,
752 realmstring
= apr_psprintf(pool
, "%s %s", sess
->realm_prefix
, realm
);
754 /* Initialize the credential baton. */
755 memset(&cred_baton
, 0, sizeof(cred_baton
));
756 cred_baton
.auth_baton
= sess
->callbacks
->auth_baton
;
757 cred_baton
.realmstring
= realmstring
;
758 cred_baton
.pool
= pool
;
760 /* Initialize the callbacks array. */
762 /* The username callback. */
763 callbacks
[0].id
= SASL_CB_AUTHNAME
;
764 callbacks
[0].proc
= get_username_cb
;
765 callbacks
[0].context
= &cred_baton
;
767 /* The password callback. */
768 callbacks
[1].id
= SASL_CB_PASS
;
769 callbacks
[1].proc
= get_password_cb
;
770 callbacks
[1].context
= &cred_baton
;
772 /* Mark the end of the array. */
773 callbacks
[2].id
= SASL_CB_LIST_END
;
774 callbacks
[2].proc
= NULL
;
775 callbacks
[2].context
= NULL
;
777 subpool
= svn_pool_create(pool
);
782 /* If last_err was set to a non-empty string, it needs to be duplicated
783 to the parent pool before the subpool is cleared. */
785 last_err
= apr_pstrdup(pool
, last_err
);
786 svn_pool_clear(subpool
);
788 SVN_ERR(new_sasl_ctx(&sasl_ctx
, sess
->is_tunneled
,
789 sess
->hostname
, local_addrport
, remote_addrport
,
790 callbacks
, sess
->conn
->pool
));
791 err
= try_auth(sess
, sasl_ctx
, &success
, &last_err
, mechstring
,
794 /* If we encountered an error while fetching credentials, that error
798 svn_error_clear(err
);
799 return cred_baton
.err
;
801 if (cred_baton
.no_more_creds
802 || (! success
&& ! err
&& ! cred_baton
.was_used
))
804 svn_error_clear(err
);
805 /* If we ran out of authentication providers, or if we got a server
806 error and our callbacks were never called, there's no point in
807 retrying authentication. Return the last error sent by the
810 return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
811 _("Authentication error from server: %s"),
813 /* Hmm, we don't have a server error. Return a generic error. */
814 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED
, NULL
,
815 _("Can't get username or password"));
819 if (err
->apr_err
== SVN_ERR_RA_SVN_NO_MECHANISMS
)
821 svn_error_clear(err
);
823 /* We could not find a supported mechanism in the list sent by the
824 server. In many cases this happens because the client is missing
825 the CRAM-MD5 or ANONYMOUS plugins, in which case we can simply use
826 the built-in implementation. In all other cases this call will be
827 useless, but hey, at least we'll get consistent error messages. */
828 return svn_ra_svn__do_internal_auth(sess
, mechlist
,
835 svn_pool_destroy(subpool
);
837 SVN_ERR(svn_ra_svn__enable_sasl_encryption(sess
->conn
, sasl_ctx
, pool
));
839 SVN_ERR(svn_auth_save_credentials(cred_baton
.iterstate
, pool
));
844 #endif /* SVN_HAVE_SASL */