/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifdef LDAP_SASLIO_HOOKS #include #include "ldap-int.h" #include "../ber/lber-int.h" #include #include #include #define SEARCH_TIMEOUT_SECS 120 #define NSLDAPI_SM_BUF 128 extern void *sasl_create_context(void); extern void sasl_free_context(void *ctx); extern int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks); extern int _sasl_client_new(void *ctx, const char *service, const char *serverFQDN, const char *iplocalport, const char *ipremoteport, const sasl_callback_t *prompt_supp, unsigned flags, sasl_conn_t **pconn); extern int _sasl_server_init(void *ctx, const sasl_callback_t *callbacks, const char *appname); extern int _sasl_server_new(void *ctx, const char *service, const char *serverFQDN, const char *user_realm, const char *iplocalport, const char *ipremoteport, const sasl_callback_t *callbacks, unsigned flags, sasl_conn_t **pconn); static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb ); /* * SASL Dependent routines * * SASL security and integrity options are supported through the * use of the extended I/O functionality. Because the extended * I/O functions may already be in use prior to enabling encryption, * when SASL encryption is enabled, these routine interpose themselves * over the existng extended I/O routines and add an additional level * of indirection. * IE: Before SASL: client->libldap->lber->extio * After SASL: client->libldap->lber->saslio->extio * Any extio functions are still used for the raw i/O [IE prldap] * but SASL will decrypt before passing to lber. * SASL cannot decrypt a stream so full packets must be read * before proceeding. */ static int nsldapi_sasl_fail() { return( SASL_FAIL ); } /* * Global SASL Init data */ static sasl_callback_t client_callbacks[] = { { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL }, { SASL_CB_GETREALM, NULL, NULL }, { SASL_CB_USER, NULL, NULL }, { SASL_CB_AUTHNAME, NULL, NULL }, { SASL_CB_PASS, NULL, NULL }, { SASL_CB_ECHOPROMPT, NULL, NULL }, { SASL_CB_NOECHOPROMPT, NULL, NULL }, { SASL_CB_LIST_END, NULL, NULL } }; static mutex_t sasl_mutex = DEFAULTMUTEX; static int nsldapi_sasl_inited = 0; static void *gctx; /* intentially not freed - avoid libsasl re-inits */ int nsldapi_sasl_init( void ) { int saslrc; mutex_lock(&sasl_mutex); if ( nsldapi_sasl_inited ) { mutex_unlock(&sasl_mutex); return( 0 ); } if ((gctx = (void *)sasl_create_context()) != NULL) { saslrc = _sasl_client_init(gctx, client_callbacks); if (saslrc == SASL_OK ) { nsldapi_sasl_inited = 1; mutex_unlock(&sasl_mutex); return( 0 ); } } mutex_unlock(&sasl_mutex); return( -1 ); } /* * SASL encryption routines */ /* * Get the 4 octet header [size] for a sasl encrypted buffer. * See RFC222 [section 3]. */ static int nsldapi_sasl_pktlen( char *buf, int maxbufsize ) { int size; #if defined( _WINDOWS ) || defined( _WIN32 ) size = ntohl(*(long *)buf); #else size = ntohl(*(uint32_t *)buf); #endif if ( size < 0 || size > maxbufsize ) { return (-1 ); } return( size + 4 ); /* include the first 4 bytes */ } static int nsldapi_sasl_read( int s, void *buf, int len, struct lextiof_socket_private *arg) { Sockbuf *sb = (Sockbuf *)arg; LDAP *ld; const char *dbuf; char *cp; int ret; unsigned dlen, blen; if (sb == NULL) { return( -1 ); } ld = (LDAP *)sb->sb_sasl_prld; if (ld == NULL) { return( -1 ); } /* Is there anything left in the existing buffer? */ if ((ret = sb->sb_sasl_ilen) > 0) { ret = (ret > len ? len : ret); SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret ); if (ret == sb->sb_sasl_ilen) { sb->sb_sasl_ilen = 0; sb->sb_sasl_iptr = NULL; } else { sb->sb_sasl_ilen -= ret; sb->sb_sasl_iptr += ret; } return( ret ); } /* buffer is empty - fill it */ cp = sb->sb_sasl_ibuf; dlen = 0; /* Read the length of the packet */ while ( dlen < 4 ) { if (sb->sb_sasl_fns.lbextiofn_read != NULL) { ret = sb->sb_sasl_fns.lbextiofn_read( s, cp, 4 - dlen, sb->sb_sasl_fns.lbextiofn_socket_arg); } else { ret = read( sb->sb_sd, cp, 4 - dlen ); } #ifdef EINTR if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) ) continue; #endif if ( ret <= 0 ) return( ret ); cp += ret; dlen += ret; } blen = 4; ret = nsldapi_sasl_pktlen( sb->sb_sasl_ibuf, sb->sb_sasl_bfsz ); if (ret < 0) { LDAP_SET_ERRNO(ld, EIO); return( -1 ); } dlen = ret - dlen; /* read the rest of the encrypted packet */ while ( dlen > 0 ) { if (sb->sb_sasl_fns.lbextiofn_read != NULL) { ret = sb->sb_sasl_fns.lbextiofn_read( s, cp, dlen, sb->sb_sasl_fns.lbextiofn_socket_arg); } else { ret = read( sb->sb_sd, cp, dlen ); } #ifdef EINTR if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) ) continue; #endif if ( ret <= 0 ) return( ret ); cp += ret; blen += ret; dlen -= ret; } /* Decode the packet */ ret = sasl_decode( sb->sb_sasl_ctx, sb->sb_sasl_ibuf, blen, &dbuf, &dlen); if ( ret != SASL_OK ) { /* sb_sasl_read: failed to decode packet, drop it, error */ sb->sb_sasl_iptr = NULL; sb->sb_sasl_ilen = 0; LDAP_SET_ERRNO(ld, EIO); return( -1 ); } /* copy decrypted packet to the input buffer */ SAFEMEMCPY( sb->sb_sasl_ibuf, dbuf, dlen ); sb->sb_sasl_iptr = sb->sb_sasl_ibuf; sb->sb_sasl_ilen = dlen; ret = (dlen > (unsigned) len ? len : dlen); SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret ); if (ret == sb->sb_sasl_ilen) { sb->sb_sasl_ilen = 0; sb->sb_sasl_iptr = NULL; } else { sb->sb_sasl_ilen -= ret; sb->sb_sasl_iptr += ret; } return( ret ); } static int nsldapi_sasl_write( int s, const void *buf, int len, struct lextiof_socket_private *arg) { Sockbuf *sb = (Sockbuf *)arg; int ret; const char *obuf, *optr; unsigned olen; if (sb == NULL) { return( -1 ); } /* encode the next packet. */ ret = sasl_encode( sb->sb_sasl_ctx, buf, (unsigned)len, &obuf, &olen); if ( ret != SASL_OK ) { /* XXX Log error? "sb_sasl_write: failed to encode packet..." */ return( -1 ); } /* Write everything now, buffer is only good until next sasl_encode */ optr = obuf; while (olen > 0) { if (sb->sb_sasl_fns.lbextiofn_write != NULL) { ret = sb->sb_sasl_fns.lbextiofn_write( s, optr, olen, sb->sb_sasl_fns.lbextiofn_socket_arg); } else { ret = write( sb->sb_sd, optr, olen); } if ( ret < 0 ) return( ret ); optr += ret; olen -= ret; } return( len ); } static int nsldapi_sasl_poll( LDAP_X_PollFD fds[], int nfds, int timeout, struct lextiof_session_private *arg ) { Sockbuf *sb = (Sockbuf *)arg; LDAP *ld; int i; if (sb == NULL) { return( -1 ); } ld = (LDAP *)sb->sb_sasl_prld; if (ld == NULL) { return( -1 ); } if (fds && nfds > 0) { for(i = 0; i < nfds; i++) { if (fds[i].lpoll_socketarg == (struct lextiof_socket_private *)sb) { fds[i].lpoll_socketarg = (struct lextiof_socket_private *) sb->sb_sasl_fns.lbextiofn_socket_arg; } } } return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout, (void *)ld->ld_sasl_io_fns.lextiof_session_arg) ); } /* no encryption indirect routines */ static int nsldapi_sasl_ne_read( int s, void *buf, int len, struct lextiof_socket_private *arg) { Sockbuf *sb = (Sockbuf *)arg; if (sb == NULL) { return( -1 ); } return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len, sb->sb_sasl_fns.lbextiofn_socket_arg) ); } static int nsldapi_sasl_ne_write( int s, const void *buf, int len, struct lextiof_socket_private *arg) { Sockbuf *sb = (Sockbuf *)arg; if (sb == NULL) { return( -1 ); } return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len, sb->sb_sasl_fns.lbextiofn_socket_arg) ); } static int nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg ) { Sockbuf *sb = (Sockbuf *)arg; LDAP *ld; if (sb == NULL) { return( -1 ); } ld = (LDAP *)sb->sb_sasl_prld; if (ld == NULL) { return( -1 ); } /* undo function pointer interposing */ ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns ); ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, (void *)&sb->sb_sasl_fns); /* undo SASL */ nsldapi_sasl_close( ld, sb ); return ( ld->ld_sasl_io_fns.lextiof_close( s, (struct lextiof_socket_private *) sb->sb_sasl_fns.lbextiofn_socket_arg ) ); } /* * install encryption routines if security has been negotiated */ static int nsldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf) { struct lber_x_ext_io_fns fns; struct ldap_x_ext_io_fns iofns; sasl_security_properties_t *secprops; int rc, value; int bufsiz; int encrypt = 0; if (ssf && *ssf) { encrypt = 1; } rc = ber_sockbuf_get_option( sb, LBER_SOCKBUF_OPT_TO_FILE_ONLY, (void *) &value); if (rc != 0 || value != 0) return( LDAP_LOCAL_ERROR ); if (encrypt) { /* initialize input buffer - use MAX SIZE to avoid reallocs */ sb->sb_sasl_ctx = (sasl_conn_t *)ctx_arg; rc = sasl_getprop( sb->sb_sasl_ctx, SASL_SEC_PROPS, (const void **)&secprops ); if (rc != SASL_OK) return( LDAP_LOCAL_ERROR ); bufsiz = secprops->maxbufsize; if (bufsiz <= 0) { return( LDAP_LOCAL_ERROR ); } if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) { return( LDAP_LOCAL_ERROR ); } sb->sb_sasl_iptr = NULL; sb->sb_sasl_bfsz = bufsiz; sb->sb_sasl_ilen = 0; } /* Reset Session then Socket Args */ /* Get old values */ (void) memset( &sb->sb_sasl_fns, 0, LBER_X_EXTIO_FNS_SIZE); sb->sb_sasl_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; rc = ber_sockbuf_get_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, (void *)&sb->sb_sasl_fns); memset( &ld->ld_sasl_io_fns, 0, sizeof(iofns)); ld->ld_sasl_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns ); if (rc != 0 ) return( LDAP_LOCAL_ERROR ); /* Set new values */ if ( ld->ld_sasl_io_fns.lextiof_read != NULL || ld->ld_sasl_io_fns.lextiof_write != NULL || ld->ld_sasl_io_fns.lextiof_poll != NULL || ld->ld_sasl_io_fns.lextiof_connect != NULL || ld->ld_sasl_io_fns.lextiof_close != NULL ) { memset( &iofns, 0, sizeof(iofns)); iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; if (encrypt) { iofns.lextiof_read = nsldapi_sasl_read; iofns.lextiof_write = nsldapi_sasl_write; iofns.lextiof_poll = nsldapi_sasl_poll; } else { iofns.lextiof_read = nsldapi_sasl_ne_read; iofns.lextiof_write = nsldapi_sasl_ne_write; iofns.lextiof_poll = nsldapi_sasl_poll; } iofns.lextiof_connect = ld->ld_sasl_io_fns.lextiof_connect; iofns.lextiof_close = nsldapi_sasl_close_socket; iofns.lextiof_newhandle = ld->ld_sasl_io_fns.lextiof_newhandle; iofns.lextiof_disposehandle = ld->ld_sasl_io_fns.lextiof_disposehandle; iofns.lextiof_session_arg = (void *) sb; /* ld->ld_sasl_io_fns.lextiof_session_arg; */ rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &iofns ); if (rc != 0 ) return( LDAP_LOCAL_ERROR ); sb->sb_sasl_prld = (void *)ld; } if (encrypt) { (void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE); fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; fns.lbextiofn_read = nsldapi_sasl_read; fns.lbextiofn_write = nsldapi_sasl_write; fns.lbextiofn_socket_arg = (void *) sb; /* (void *)sb->sb_sasl_fns.lbextiofn_socket_arg; */ rc = ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, (void *)&fns); if (rc != 0) return( LDAP_LOCAL_ERROR ); } return( LDAP_SUCCESS ); } static int nsldapi_sasl_cvterrno( LDAP *ld, int err ) { int rc = LDAP_LOCAL_ERROR; switch (err) { case SASL_OK: rc = LDAP_SUCCESS; break; case SASL_NOMECH: rc = LDAP_AUTH_UNKNOWN; break; case SASL_BADSERV: rc = LDAP_CONNECT_ERROR; break; case SASL_DISABLED: case SASL_ENCRYPT: case SASL_EXPIRED: case SASL_NOUSERPASS: case SASL_NOVERIFY: case SASL_PWLOCK: case SASL_TOOWEAK: case SASL_UNAVAIL: case SASL_WEAKPASS: rc = LDAP_INAPPROPRIATE_AUTH; break; case SASL_BADAUTH: case SASL_NOAUTHZ: rc = LDAP_INVALID_CREDENTIALS; break; case SASL_NOMEM: rc = LDAP_NO_MEMORY; break; case SASL_NOUSER: rc = LDAP_NO_SUCH_OBJECT; break; case SASL_CONTINUE: case SASL_FAIL: case SASL_INTERACT: default: rc = LDAP_LOCAL_ERROR; break; } LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); return( rc ); } int nsldapi_sasl_open(LDAP *ld) { Sockbuf *sb; char * host; int saslrc; sasl_conn_t *ctx; if (ld == NULL) { return( LDAP_LOCAL_ERROR ); } if (ld->ld_defconn == NULL) { LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); return( LDAP_LOCAL_ERROR ); } sb = ld->ld_defconn->lconn_sb; host = ld->ld_defhost; if ( sb == NULL || host == NULL ) { LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); return( LDAP_LOCAL_ERROR ); } /* SASL is not properly initialized */ mutex_lock(&sasl_mutex); if (gctx == NULL) { LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); mutex_unlock(&sasl_mutex); return( LDAP_LOCAL_ERROR ); } saslrc = _sasl_client_new(gctx, "ldap", host, NULL, NULL, /* iplocal ipremote strings currently unused */ NULL, 0, &ctx ); if ( saslrc != SASL_OK ) { mutex_unlock(&sasl_mutex); return( nsldapi_sasl_cvterrno( ld, saslrc ) ); } sb->sb_sasl_ctx = (void *)ctx; mutex_unlock(&sasl_mutex); return( LDAP_SUCCESS ); } static void destroy_sasliobuf(Sockbuf *sb) { if (sb != NULL && sb->sb_sasl_ibuf != NULL) { NSLDAPI_FREE(sb->sb_sasl_ibuf); sb->sb_sasl_ibuf = NULL; } } static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb ) { sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx; destroy_sasliobuf(sb); if( ctx != NULL ) { sasl_dispose( &ctx ); sb->sb_sasl_ctx = NULL; } return( LDAP_SUCCESS ); } static int nsldapi_sasl_do_bind( LDAP *ld, const char *dn, const char *mechs, unsigned flags, LDAP_SASL_INTERACT_PROC *callback, void *defaults, LDAPControl **sctrl, LDAPControl **cctrl ) { sasl_interact_t *prompts = NULL; sasl_conn_t *ctx; sasl_ssf_t *ssf = NULL; const char *mech = NULL; int saslrc, rc; struct berval ccred; unsigned credlen; if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) { LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); return( LDAP_NOT_SUPPORTED ); } /* shouldn't happen */ if (callback == NULL) { return( LDAP_LOCAL_ERROR ); } if ( ld->ld_defconn == NULL || ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) { rc = nsldapi_open_ldap_defconn( ld ); if( rc < 0 ) { return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); } } /* should have a valid ld connection - now create sasl connection */ if ((rc = nsldapi_sasl_open(ld)) != LDAP_SUCCESS) { LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); return( rc ); } /* expect context to be initialized when connection is open */ ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx; if( ctx == NULL ) { LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); return( LDAP_LOCAL_ERROR ); } /* (re)set security properties */ sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops ); ccred.bv_val = NULL; ccred.bv_len = 0; do { saslrc = sasl_client_start( ctx, mechs, &prompts, (const char **)&ccred.bv_val, &credlen, &mech ); LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n", (mech ? mech : ""), 0, 0 ); if( saslrc == SASL_INTERACT && (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) { break; } } while ( saslrc == SASL_INTERACT ); ccred.bv_len = credlen; if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { return( nsldapi_sasl_cvterrno( ld, saslrc ) ); } do { struct berval *scred; scred = NULL; /* notify server of a sasl bind step */ rc = ldap_sasl_bind_s(ld, dn, mech, &ccred, sctrl, cctrl, &scred); if ( ccred.bv_val != NULL ) { ccred.bv_val = NULL; } if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { if( scred && scred->bv_len ) { /* and server provided us with data? */ ber_bvfree( scred ); } return( rc ); } if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) { /* we're done, no need to step */ if( scred && scred->bv_len ) { /* but server provided us with data! */ ber_bvfree( scred ); LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); return( LDAP_LOCAL_ERROR ); } break; } /* perform the next step of the sasl bind */ do { saslrc = sasl_client_step( ctx, (scred == NULL) ? NULL : scred->bv_val, (scred == NULL) ? 0 : scred->bv_len, &prompts, (const char **)&ccred.bv_val, &credlen ); if( saslrc == SASL_INTERACT && (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) { break; } } while ( saslrc == SASL_INTERACT ); ccred.bv_len = credlen; ber_bvfree( scred ); if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { return( nsldapi_sasl_cvterrno( ld, saslrc ) ); } } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); if ( rc != LDAP_SUCCESS ) { return( rc ); } if ( saslrc != SASL_OK ) { return( nsldapi_sasl_cvterrno( ld, saslrc ) ); } saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf ); if( saslrc == SASL_OK ) { rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf); } return( rc ); } #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER /* * Get available SASL Mechanisms supported by the server */ static int nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech ) { char *attr[] = { "supportedSASLMechanisms", NULL }; char **values, **v, *mech, *m; LDAPMessage *res, *e; struct timeval timeout; int slen, rc; if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { return( LDAP_PARAM_ERROR ); } timeout.tv_sec = SEARCH_TIMEOUT_SECS; timeout.tv_usec = 0; rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE, "objectclass=*", attr, 0, &timeout, &res ); if ( rc != LDAP_SUCCESS ) { return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); } e = ldap_first_entry( ld, res ); if ( e == NULL ) { ldap_msgfree( res ); if ( ld->ld_errno == LDAP_SUCCESS ) { LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL ); } return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); } values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); if ( values == NULL ) { ldap_msgfree( res ); LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL ); return( LDAP_NO_SUCH_ATTRIBUTE ); } slen = 0; for(v = values; *v != NULL; v++ ) { slen += strlen(*v) + 1; } if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) { ldap_value_free( values ); ldap_msgfree( res ); LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); return( LDAP_NO_MEMORY ); } m = mech; for(v = values; *v; v++) { if (v != values) { *m++ = ' '; } slen = strlen(*v); strncpy(m, *v, slen); m += slen; } *m = '\0'; ldap_value_free( values ); ldap_msgfree( res ); *pmech = mech; return( LDAP_SUCCESS ); } #endif int nsldapi_sasl_secprops( const char *in, sasl_security_properties_t *secprops ) { int i; char **props = NULL; char *inp; unsigned sflags = 0; sasl_ssf_t max_ssf = 0; sasl_ssf_t min_ssf = 0; unsigned maxbufsize = 0; int got_sflags = 0; int got_max_ssf = 0; int got_min_ssf = 0; int got_maxbufsize = 0; if (in == NULL) { return LDAP_PARAM_ERROR; } inp = nsldapi_strdup(in); if (inp == NULL) { return LDAP_PARAM_ERROR; } props = ldap_str2charray( inp, "," ); NSLDAPI_FREE( inp ); if( props == NULL || secprops == NULL ) { return LDAP_PARAM_ERROR; } for( i=0; props[i]; i++ ) { if( strcasecmp(props[i], "none") == 0 ) { got_sflags++; } else if( strcasecmp(props[i], "noactive") == 0 ) { got_sflags++; sflags |= SASL_SEC_NOACTIVE; } else if( strcasecmp(props[i], "noanonymous") == 0 ) { got_sflags++; sflags |= SASL_SEC_NOANONYMOUS; } else if( strcasecmp(props[i], "nodict") == 0 ) { got_sflags++; sflags |= SASL_SEC_NODICTIONARY; } else if( strcasecmp(props[i], "noplain") == 0 ) { got_sflags++; sflags |= SASL_SEC_NOPLAINTEXT; } else if( strcasecmp(props[i], "forwardsec") == 0 ) { got_sflags++; sflags |= SASL_SEC_FORWARD_SECRECY; } else if( strcasecmp(props[i], "passcred") == 0 ) { got_sflags++; sflags |= SASL_SEC_PASS_CREDENTIALS; } else if( strncasecmp(props[i], "minssf=", sizeof("minssf")) == 0 ) { if( isdigit( props[i][sizeof("minssf")] ) ) { got_min_ssf++; min_ssf = atoi( &props[i][sizeof("minssf")] ); } else { return LDAP_NOT_SUPPORTED; } } else if( strncasecmp(props[i], "maxssf=", sizeof("maxssf")) == 0 ) { if( isdigit( props[i][sizeof("maxssf")] ) ) { got_max_ssf++; max_ssf = atoi( &props[i][sizeof("maxssf")] ); } else { return LDAP_NOT_SUPPORTED; } } else if( strncasecmp(props[i], "maxbufsize=", sizeof("maxbufsize")) == 0 ) { if( isdigit( props[i][sizeof("maxbufsize")] ) ) { got_maxbufsize++; maxbufsize = atoi( &props[i][sizeof("maxbufsize")] ); if( maxbufsize && (( maxbufsize < SASL_MIN_BUFF_SIZE ) || (maxbufsize > SASL_MAX_BUFF_SIZE ))) { return( LDAP_PARAM_ERROR ); } } else { return( LDAP_NOT_SUPPORTED ); } } else { return( LDAP_NOT_SUPPORTED ); } } if(got_sflags) { secprops->security_flags = sflags; } if(got_min_ssf) { secprops->min_ssf = min_ssf; } if(got_max_ssf) { secprops->max_ssf = max_ssf; } if(got_maxbufsize) { secprops->maxbufsize = maxbufsize; } ldap_charray_free( props ); return( LDAP_SUCCESS ); } /* * SASL Authentication Interface: ldap_sasl_interactive_bind_s * * This routine takes a DN, SASL mech list, and a SASL callback * and performs the necessary sequencing to complete a SASL bind * to the LDAP connection ld. The user provided callback can * use an optionally provided set of default values to complete * any necessary interactions. * * Currently inpose the following restrictions: * A mech list must be provided, only LDAP_SASL_INTERACTIVE * mode is supported */ int LDAP_CALL ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn, const char *saslMechanism, LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags, LDAP_SASL_INTERACT_PROC *callback, void *defaults ) { #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER char *smechs; #endif int rc; LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0); if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { return( LDAP_PARAM_ERROR ); } if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) { return( LDAP_PARAM_ERROR ); } LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK ); if( saslMechanism == NULL || *saslMechanism == '\0' ) { #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER rc = nsldapi_get_sasl_mechs( ld, &smechs ); if( rc != LDAP_SUCCESS ) { LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); return( rc ); } saslMechanism = smechs; #else LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); return( LDAP_PARAM_ERROR ); #endif } /* initialize SASL library */ if ( nsldapi_sasl_init() < 0 ) { return( LDAP_PARAM_ERROR ); } rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism, flags, callback, defaults, sctrl, cctrl); LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); return( rc ); } #endif