1 /* $OpenLDAP: pkg/ldap/libraries/libldap/sasl.c,v 1.64.2.4 2008/02/11 23:26:41 kurt Exp $ */ 2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1998-2008 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15 16 /* 17 * BindRequest ::= SEQUENCE { 18 * version INTEGER, 19 * name DistinguishedName, -- who 20 * authentication CHOICE { 21 * simple [0] OCTET STRING -- passwd 22 * krbv42ldap [1] OCTET STRING -- OBSOLETE 23 * krbv42dsa [2] OCTET STRING -- OBSOLETE 24 * sasl [3] SaslCredentials -- LDAPv3 25 * } 26 * } 27 * 28 * BindResponse ::= SEQUENCE { 29 * COMPONENTS OF LDAPResult, 30 * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 31 * } 32 * 33 */ 34 35 #include "portable.h" 36 37 #include <stdio.h> 38 39 #include <ac/socket.h> 40 #include <ac/stdlib.h> 41 #include <ac/string.h> 42 #include <ac/time.h> 43 #include <ac/errno.h> 44 45 #include "ldap-int.h" 46 47 /* 48 * ldap_sasl_bind - bind to the ldap server (and X.500). 49 * The dn (usually NULL), mechanism, and credentials are provided. 50 * The message id of the request initiated is provided upon successful 51 * (LDAP_SUCCESS) return. 52 * 53 * Example: 54 * ldap_sasl_bind( ld, NULL, "mechanism", 55 * cred, NULL, NULL, &msgid ) 56 */ 57 58 int 59 ldap_sasl_bind( 60 LDAP *ld, 61 LDAP_CONST char *dn, 62 LDAP_CONST char *mechanism, 63 struct berval *cred, 64 LDAPControl **sctrls, 65 LDAPControl **cctrls, 66 int *msgidp ) 67 { 68 BerElement *ber; 69 int rc; 70 ber_int_t id; 71 72 Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 ); 73 74 assert( ld != NULL ); 75 assert( LDAP_VALID( ld ) ); 76 assert( msgidp != NULL ); 77 78 /* check client controls */ 79 rc = ldap_int_client_controls( ld, cctrls ); 80 if( rc != LDAP_SUCCESS ) return rc; 81 82 if( mechanism == LDAP_SASL_SIMPLE ) { 83 if( dn == NULL && cred != NULL && cred->bv_len ) { 84 /* use default binddn */ 85 dn = ld->ld_defbinddn; 86 } 87 88 } else if( ld->ld_version < LDAP_VERSION3 ) { 89 ld->ld_errno = LDAP_NOT_SUPPORTED; 90 return ld->ld_errno; 91 } 92 93 if ( dn == NULL ) { 94 dn = ""; 95 } 96 97 /* create a message to send */ 98 if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { 99 ld->ld_errno = LDAP_NO_MEMORY; 100 return ld->ld_errno; 101 } 102 103 assert( LBER_VALID( ber ) ); 104 105 LDAP_NEXT_MSGID( ld, id ); 106 if( mechanism == LDAP_SASL_SIMPLE ) { 107 /* simple bind */ 108 rc = ber_printf( ber, "{it{istON}" /*}*/, 109 id, LDAP_REQ_BIND, 110 ld->ld_version, dn, LDAP_AUTH_SIMPLE, 111 cred ); 112 113 } else if ( cred == NULL || cred->bv_val == NULL ) { 114 /* SASL bind w/o credentials */ 115 rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/, 116 id, LDAP_REQ_BIND, 117 ld->ld_version, dn, LDAP_AUTH_SASL, 118 mechanism ); 119 120 } else { 121 /* SASL bind w/ credentials */ 122 rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/, 123 id, LDAP_REQ_BIND, 124 ld->ld_version, dn, LDAP_AUTH_SASL, 125 mechanism, cred ); 126 } 127 128 if( rc == -1 ) { 129 ld->ld_errno = LDAP_ENCODING_ERROR; 130 ber_free( ber, 1 ); 131 return( -1 ); 132 } 133 134 /* Put Server Controls */ 135 if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { 136 ber_free( ber, 1 ); 137 return ld->ld_errno; 138 } 139 140 if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { 141 ld->ld_errno = LDAP_ENCODING_ERROR; 142 ber_free( ber, 1 ); 143 return ld->ld_errno; 144 } 145 146 147 /* send the message */ 148 *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id ); 149 150 if(*msgidp < 0) 151 return ld->ld_errno; 152 153 return LDAP_SUCCESS; 154 } 155 156 157 int 158 ldap_sasl_bind_s( 159 LDAP *ld, 160 LDAP_CONST char *dn, 161 LDAP_CONST char *mechanism, 162 struct berval *cred, 163 LDAPControl **sctrls, 164 LDAPControl **cctrls, 165 struct berval **servercredp ) 166 { 167 int rc, msgid; 168 LDAPMessage *result; 169 struct berval *scredp = NULL; 170 171 Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 ); 172 173 /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ 174 if( servercredp != NULL ) { 175 if (ld->ld_version < LDAP_VERSION3) { 176 ld->ld_errno = LDAP_NOT_SUPPORTED; 177 return ld->ld_errno; 178 } 179 *servercredp = NULL; 180 } 181 182 rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid ); 183 184 if ( rc != LDAP_SUCCESS ) { 185 return( rc ); 186 } 187 188 #ifdef LDAP_CONNECTIONLESS 189 if (LDAP_IS_UDP(ld)) { 190 return( rc ); 191 } 192 #endif 193 194 if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { 195 return( ld->ld_errno ); /* ldap_result sets ld_errno */ 196 } 197 198 /* parse the results */ 199 scredp = NULL; 200 if( servercredp != NULL ) { 201 rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 ); 202 } 203 204 if ( rc != LDAP_SUCCESS ) { 205 ldap_msgfree( result ); 206 return( rc ); 207 } 208 209 rc = ldap_result2error( ld, result, 1 ); 210 211 if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) { 212 if( servercredp != NULL ) { 213 *servercredp = scredp; 214 scredp = NULL; 215 } 216 } 217 218 if ( scredp != NULL ) { 219 ber_bvfree(scredp); 220 } 221 222 return rc; 223 } 224 225 226 /* 227 * Parse BindResponse: 228 * 229 * BindResponse ::= [APPLICATION 1] SEQUENCE { 230 * COMPONENTS OF LDAPResult, 231 * serverSaslCreds [7] OCTET STRING OPTIONAL } 232 * 233 * LDAPResult ::= SEQUENCE { 234 * resultCode ENUMERATED, 235 * matchedDN LDAPDN, 236 * errorMessage LDAPString, 237 * referral [3] Referral OPTIONAL } 238 */ 239 240 int 241 ldap_parse_sasl_bind_result( 242 LDAP *ld, 243 LDAPMessage *res, 244 struct berval **servercredp, 245 int freeit ) 246 { 247 ber_int_t errcode; 248 struct berval* scred; 249 250 ber_tag_t tag; 251 BerElement *ber; 252 253 Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 ); 254 255 assert( ld != NULL ); 256 assert( LDAP_VALID( ld ) ); 257 assert( res != NULL ); 258 259 if( servercredp != NULL ) { 260 if( ld->ld_version < LDAP_VERSION2 ) { 261 return LDAP_NOT_SUPPORTED; 262 } 263 *servercredp = NULL; 264 } 265 266 if( res->lm_msgtype != LDAP_RES_BIND ) { 267 ld->ld_errno = LDAP_PARAM_ERROR; 268 return ld->ld_errno; 269 } 270 271 scred = NULL; 272 273 if ( ld->ld_error ) { 274 LDAP_FREE( ld->ld_error ); 275 ld->ld_error = NULL; 276 } 277 if ( ld->ld_matched ) { 278 LDAP_FREE( ld->ld_matched ); 279 ld->ld_matched = NULL; 280 } 281 282 /* parse results */ 283 284 ber = ber_dup( res->lm_ber ); 285 286 if( ber == NULL ) { 287 ld->ld_errno = LDAP_NO_MEMORY; 288 return ld->ld_errno; 289 } 290 291 if ( ld->ld_version < LDAP_VERSION2 ) { 292 tag = ber_scanf( ber, "{iA}", 293 &errcode, &ld->ld_error ); 294 295 if( tag == LBER_ERROR ) { 296 ber_free( ber, 0 ); 297 ld->ld_errno = LDAP_DECODING_ERROR; 298 return ld->ld_errno; 299 } 300 301 } else { 302 ber_len_t len; 303 304 tag = ber_scanf( ber, "{eAA" /*}*/, 305 &errcode, &ld->ld_matched, &ld->ld_error ); 306 307 if( tag == LBER_ERROR ) { 308 ber_free( ber, 0 ); 309 ld->ld_errno = LDAP_DECODING_ERROR; 310 return ld->ld_errno; 311 } 312 313 tag = ber_peek_tag(ber, &len); 314 315 if( tag == LDAP_TAG_REFERRAL ) { 316 /* skip 'em */ 317 if( ber_scanf( ber, "x" ) == LBER_ERROR ) { 318 ber_free( ber, 0 ); 319 ld->ld_errno = LDAP_DECODING_ERROR; 320 return ld->ld_errno; 321 } 322 323 tag = ber_peek_tag(ber, &len); 324 } 325 326 if( tag == LDAP_TAG_SASL_RES_CREDS ) { 327 if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) { 328 ber_free( ber, 0 ); 329 ld->ld_errno = LDAP_DECODING_ERROR; 330 return ld->ld_errno; 331 } 332 } 333 } 334 335 ber_free( ber, 0 ); 336 337 if ( servercredp != NULL ) { 338 *servercredp = scred; 339 340 } else if ( scred != NULL ) { 341 ber_bvfree( scred ); 342 } 343 344 ld->ld_errno = errcode; 345 346 if ( freeit ) { 347 ldap_msgfree( res ); 348 } 349 350 return( LDAP_SUCCESS ); 351 } 352 353 int 354 ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist ) 355 { 356 /* we need to query the server for supported mechs anyway */ 357 LDAPMessage *res, *e; 358 char *attrs[] = { "supportedSASLMechanisms", NULL }; 359 char **values, *mechlist; 360 int rc; 361 362 Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 ); 363 364 rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE, 365 NULL, attrs, 0, &res ); 366 367 if ( rc != LDAP_SUCCESS ) { 368 return ld->ld_errno; 369 } 370 371 e = ldap_first_entry( ld, res ); 372 if ( e == NULL ) { 373 ldap_msgfree( res ); 374 if ( ld->ld_errno == LDAP_SUCCESS ) { 375 ld->ld_errno = LDAP_NO_SUCH_OBJECT; 376 } 377 return ld->ld_errno; 378 } 379 380 values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); 381 if ( values == NULL ) { 382 ldap_msgfree( res ); 383 ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE; 384 return ld->ld_errno; 385 } 386 387 mechlist = ldap_charray2str( values, " " ); 388 if ( mechlist == NULL ) { 389 LDAP_VFREE( values ); 390 ldap_msgfree( res ); 391 ld->ld_errno = LDAP_NO_MEMORY; 392 return ld->ld_errno; 393 } 394 395 LDAP_VFREE( values ); 396 ldap_msgfree( res ); 397 398 *pmechlist = mechlist; 399 400 return LDAP_SUCCESS; 401 } 402 403 /* 404 * ldap_sasl_interactive_bind_s - interactive SASL authentication 405 * 406 * This routine uses interactive callbacks. 407 * 408 * LDAP_SUCCESS is returned upon success, the ldap error code 409 * otherwise. 410 */ 411 int 412 ldap_sasl_interactive_bind_s( 413 LDAP *ld, 414 LDAP_CONST char *dn, /* usually NULL */ 415 LDAP_CONST char *mechs, 416 LDAPControl **serverControls, 417 LDAPControl **clientControls, 418 unsigned flags, 419 LDAP_SASL_INTERACT_PROC *interact, 420 void *defaults ) 421 { 422 int rc; 423 char *smechs = NULL; 424 425 #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL ) 426 ldap_pvt_thread_mutex_lock( &ldap_int_sasl_mutex ); 427 #endif 428 #ifdef LDAP_CONNECTIONLESS 429 if( LDAP_IS_UDP(ld) ) { 430 /* Just force it to simple bind, silly to make the user 431 * ask all the time. No, we don't ever actually bind, but I'll 432 * let the final bind handler take care of saving the cdn. 433 */ 434 rc = ldap_simple_bind( ld, dn, NULL ); 435 rc = rc < 0 ? rc : 0; 436 goto done; 437 } else 438 #endif 439 440 #ifdef HAVE_CYRUS_SASL 441 if( mechs == NULL || *mechs == '\0' ) { 442 mechs = ld->ld_options.ldo_def_sasl_mech; 443 } 444 #endif 445 446 if( mechs == NULL || *mechs == '\0' ) { 447 rc = ldap_pvt_sasl_getmechs( ld, &smechs ); 448 if( rc != LDAP_SUCCESS ) { 449 goto done; 450 } 451 452 Debug( LDAP_DEBUG_TRACE, 453 "ldap_sasl_interactive_bind_s: server supports: %s\n", 454 smechs, 0, 0 ); 455 456 mechs = smechs; 457 458 } else { 459 Debug( LDAP_DEBUG_TRACE, 460 "ldap_sasl_interactive_bind_s: user selected: %s\n", 461 mechs, 0, 0 ); 462 } 463 464 rc = ldap_int_sasl_bind( ld, dn, mechs, 465 serverControls, clientControls, 466 flags, interact, defaults ); 467 468 done: 469 #if defined( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL ) 470 ldap_pvt_thread_mutex_unlock( &ldap_int_sasl_mutex ); 471 #endif 472 if ( smechs ) LDAP_FREE( smechs ); 473 474 return rc; 475 } 476