diff -urN cyrus-sasl-2.1.20.orig/configure.in cyrus-sasl-2.1.20/configure.in --- cyrus-sasl-2.1.20.orig/configure.in 2005-02-09 07:59:25.964620000 +0000 +++ cyrus-sasl-2.1.20/configure.in 2005-02-09 08:00:56.768816632 +0000 @@ -543,26 +543,60 @@ ntlm=$enableval, ntlm=no) -if test "$with_openssl" = no; then +AC_ARG_WITH(ntlm_impl, [ --with-ntlm_impl={cyrus|samba} Choose specific NTLMSSP implementation [cyrus] ], + ntlm_impl=$withval, + ntlm_impl=cyrus) + +if test "$with_openssl" = no -a "$ntlm_impl" = cyrus; then AC_WARN([OpenSSL not found -- NTLM will be disabled]) ntlm=no fi AC_MSG_CHECKING(NTLM) if test "$ntlm" != no; then - AC_MSG_RESULT(enabled) - NTLM_LIBS="-lcrypto $LIB_RSAREF" + AC_MSG_RESULT(with implementation ${ntlm_impl}) + + if test "$ntlm_impl" = samba; then + NTLM_LIBS="" + NTLM_OBJS="ntlm_samba.lo" + NTLM_STATIC_OBJS="ntlm_samba.o" + else + NTLM_LIBS="-lcrypto $LIB_RSAREF" + NTLM_OBJS="ntlm.lo" + NTLM_STATIC_OBJS="ntlm.o" + fi + AC_SUBST(NTLM_LIBS) + AC_SUBST(NTLM_OBJS) SASL_MECHS="$SASL_MECHS libntlm.la" if test "$enable_static" = yes; then - SASL_STATIC_OBJS="$SASL_STATIC_OBJS ../plugins/ntlm.o" + SASL_STATIC_OBJS="$SASL_STATIC_OBJS ../plugins/$NTLM_STATIC_OBJS" AC_DEFINE(STATIC_NTLM,[],[Link NTLM Staticly]) fi else AC_MSG_RESULT(disabled) fi +dnl GSSSPNEGO +AC_ARG_ENABLE(gssspnego, [ --enable-gssspnego enable unsupported GSSSPNEGO authentication [no] ], + gssspnego=$enableval, + gssspnego=no) + +AC_MSG_CHECKING(GSSSPNEGO) +if test "$gssspnego" != no; then + AC_MSG_RESULT(enabled) + AC_SUBST(GSSSPNEGO_LIBS) + + SASL_MECHS="$SASL_MECHS libgssspnego.la" + if test "$enable_static" = yes; then + SASL_STATIC_OBJS="$SASL_STATIC_OBJS ../plugins/gssspnego.o" + AC_DEFINE(STATIC_GSSSPNEGO,[],[Link GSSSPNEGO Staticly]) + fi +else + AC_MSG_RESULT(disabled) +fi + # make the option show up so people don't whine that it is only in the # saslauthd configure script --help diff -urN cyrus-sasl-2.1.20.orig/plugins/Makefile.am cyrus-sasl-2.1.20/plugins/Makefile.am --- cyrus-sasl-2.1.20.orig/plugins/Makefile.am 2004-10-24 21:05:20.000000000 +0100 +++ cyrus-sasl-2.1.20/plugins/Makefile.am 2005-02-09 08:01:41.144070568 +0000 @@ -55,6 +55,7 @@ login_version = 2:20:0 plain_version = 2:20:0 ntlm_version = 2:20:0 +gssspnego_version = 2:20:0 otp_version = 2:20:0 sql_version = 2:20:0 srp_version = 2:20:0 @@ -77,7 +78,7 @@ sasl_LTLIBRARIES = @SASL_MECHS@ EXTRA_LTLIBRARIES = libplain.la libanonymous.la libkerberos4.la libcrammd5.la \ libgssapiv2.la libdigestmd5.la liblogin.la libsrp.la libotp.la \ - libntlm.la libsasldb.la libsql.la + libntlm.la libgssspnego.la libsasldb.la libsql.la libplain_la_SOURCES = plain.c plain_init.c $(common_sources) libplain_la_LDFLAGS = -version-info $(plain_version) @@ -124,10 +125,16 @@ libotp_la_DEPENDENCIES = $(COMPAT_OBJS) libotp_la_LIBADD = $(OTP_LIBS) $(COMPAT_OBJS) -libntlm_la_SOURCES = ntlm.c ntlm_init.c $(common_sources) +libntlm_la_SOURCES = smb_helper.c smb_helper.h ntlm_init.c $(common_sources) +EXTRA_libntlm_la_SOURCES = ntlm.c ntlm_samba.c libntlm_la_LDFLAGS = -version-info $(ntlm_version) -libntlm_la_DEPENDENCIES = $(COMPAT_OBJS) -libntlm_la_LIBADD = $(NTLM_LIBS) $(COMPAT_OBJS) +libntlm_la_DEPENDENCIES = $(COMPAT_OBJS) $(NTLM_OBJS) +libntlm_la_LIBADD = $(NTLM_LIBS) $(COMPAT_OBJS) $(NTLM_OBJS) + +libgssspnego_la_SOURCES = smb_helper.c smb_helper.h gssspnego.c gssspnego_init.c $(common_sources) +libgssspnego_la_LDFLAGS = -version-info $(gssspnego_version) +libgssspnego_la_DEPENDENCIES = $(COMPAT_OBJS) +libgssspnego_la_LIBADD = $(GSSSPNEGO_LIBS) $(COMPAT_OBJS) # Auxprop Plugins libsasldb_la_SOURCES = sasldb.c sasldb_init.c $(common_sources) @@ -145,7 +152,7 @@ init_src=anonymous_init.c crammd5_init.c digestmd5_init.c gssapiv2_init.c \ kerberos4_init.c login_init.c plain_init.c srp_init.c otp_init.c ntlm_init.c \ -sasldb_init.c sql_init.c +gssspnego_init.c sasldb_init.c sql_init.c CLEANFILES=$(init_src) diff -urN cyrus-sasl-2.1.20.orig/plugins/gssspnego.c cyrus-sasl-2.1.20/plugins/gssspnego.c --- cyrus-sasl-2.1.20.orig/plugins/gssspnego.c 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-sasl-2.1.20/plugins/gssspnego.c 2005-02-09 08:00:56.769816480 +0000 @@ -0,0 +1,557 @@ +/* GSS SPNEGO plugin + * Volker Lendecke + */ +/* + * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "plugin_common.h" +#include "smb_helper.h" + +#ifdef macintosh +#include +#endif + +/***************************** Common Section *****************************/ + +static const char plugin_id[] = "$Id: gssspnego.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $"; + +/***************************** Server Section *****************************/ + +struct gssspnego_context { + struct smb_helper *helper; +}; + +static int gssspnego_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + char * argv[] = { "ntlm_auth", + "--helper-protocol=gss-spnego", + NULL }; + + struct gssspnego_context *context = + malloc(sizeof(struct gssspnego_context)); + + if (context == NULL) { + syslog(LOG_DEBUG, "gssspnego_server_mech_new: No memory\n"); + MEMERROR(sparams->utils); + return SASL_NOMEM; + } + + memset(context, 0, sizeof(struct gssspnego_context)); + + *conn_context = context; + + return fork_helper(&context->helper, "ntlm_auth", + argv, sparams->utils); +} + +static int gssspnego_server_mech_step(void *conn_context, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + struct gssspnego_context *context = + (struct gssspnego_context *)conn_context; + + unsigned char childbuf[1025]; + unsigned childbuflen; + + unsigned char *childarg; + unsigned base64len; + + static unsigned char bin[1025]; + + int result; + + if (clientinlen == 0) { + + /* This should only happen on the first request, we + need the initial mechanism offer from the child. */ + + strncpy(childbuf, "YR", sizeof(childbuf)-1); + childbuflen = strlen(childbuf); + + } else { + + strncpy(childbuf, "KK ", sizeof(childbuf)-1); + + if (params->utils->encode64(clientin, clientinlen, + childbuf+3, sizeof(childbuf)-4, + &childbuflen) != SASL_OK) { + syslog(LOG_DEBUG, + "Could not convert clientin to base64\n"); + return SASL_FAIL; + } + } + + if ((result = interact_helper(context->helper, + childbuf, + sizeof(childbuf), + &childbuflen)) != SASL_OK) + return result; + + /* The child's reply contains 3 parts: + - The code: TT, AF or NA + - The blob to send to the client, coded in base64 + - The argument: + For TT it's a dummy '*' + For AF it's domain\\user + For NA it's the NT error code + */ + + childarg = strchr(childbuf+3, ' '); + + if (childarg == NULL) { + syslog(LOG_DEBUG, "Got invalid response from child\n"); + return SASL_FAIL; + } + + base64len = (childarg - childbuf) - 3; + + if (params->utils->decode64(childbuf+3, base64len, + bin, sizeof(bin)-1, + serveroutlen) != SASL_OK) { + syslog(LOG_DEBUG, "Could not decode child's base64\n"); + return SASL_FAIL; + } + *serverout = bin; + childarg++; + + if ( strncmp(childbuf, "TT ", 3) == 0) { + /* Next round */ + return SASL_CONTINUE; + } + if (strncmp(childbuf, "NA ", 3) == 0) { + /* Not Authenticated */ + kill_helper(context->helper); + return SASL_BADAUTH; + } + + if (strncmp(childbuf, "AF ", 3) == 0) { + /* Authentication Fine */ + char *domain = childarg; + char *user = strchr(domain, '\\'); + int status; + + kill_helper(context->helper); + + if (user == NULL) { + syslog(LOG_DEBUG, + "Could not find user/domain in AF\n"); + return SASL_FAIL; + } + *user++ = 0; + if ( (status = params->canon_user(params->utils->conn, + user, 0, SASL_CU_AUTHID, + oparams)) != SASL_OK ) { + syslog(LOG_DEBUG, "canon_user for AUTHID failed\n"); + return status; + } + + if ( (status = params->canon_user(params->utils->conn, + user, 0, SASL_CU_AUTHZID, + oparams)) != SASL_OK ) { + syslog(LOG_DEBUG, "canon_user for AUTHZID failed\n"); + return status; + } + + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + + return SASL_OK; + } + + syslog(LOG_DEBUG, "Child's response unknown\n"); + return SASL_FAIL; +} + +static void gssspnego_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils __attribute__((unused))) +{ + struct gssspnego_context *context = + (struct gssspnego_context *)conn_context; + + kill_helper(context->helper); + + return; +} + +static sasl_server_plug_t gssspnego_server_plugins[] = +{ + { + "GSS-SPNEGO", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS + | SASL_SEC_NOPLAINTEXT, /* security_flags */ + SASL_FEAT_SERVER_FIRST, /* features */ + NULL, /* glob_context */ + &gssspnego_server_mech_new, /* mech_new */ + &gssspnego_server_mech_step, /* mech_step */ + &gssspnego_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech_avail */ + NULL /* spare */ + } +}; + +int gssspnego_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR(utils, "GSSSPNEGO version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = gssspnego_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +static int gssspnego_new_client_helper(struct gssspnego_context *context, + sasl_client_params_t *params, + sasl_interact_t **prompt_need, + sasl_out_params_t *oparams) +{ + int auth_result = SASL_OK; + int domain_result = SASL_OK; + static const char *authid = NULL; + static const char *domain = NULL; + + char child_user_arg[512]; + char child_domain_arg[512]; + + int result; + + char *argv[] = { "ntlm_auth", + "--helper-protocol=gss-spnego-client", + child_user_arg, + child_domain_arg, + NULL }; + + if (authid == NULL) { + auth_result = _plug_get_authid(params->utils, + &authid, prompt_need); + + if ( (auth_result != SASL_OK) && + (auth_result != SASL_INTERACT) && + (authid == NULL) ) + return auth_result; + } + + if (domain == NULL) { + domain_result = _plug_get_realm(params->utils, NULL, + &domain, prompt_need); + if ( (domain_result != SASL_OK) && + (domain_result != SASL_INTERACT) && + (domain == NULL) ) + return domain_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + if ( (auth_result == SASL_INTERACT) || + (domain_result == SASL_INTERACT) ) { + result = _plug_make_prompts(params->utils, prompt_need, + NULL, NULL, + (auth_result == SASL_INTERACT) ? + "Username" : NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, + (domain_result == SASL_INTERACT) ? + "Realm (Domain)" : NULL, NULL); + + if (result != SASL_OK) + return result; + return SASL_INTERACT; + } + + oparams->user = strdup(authid); + oparams->ulen = strlen(authid); + oparams->authid = strdup(authid); + oparams->alen = oparams->ulen; + + snprintf(child_user_arg, sizeof(child_user_arg)-1, + "--username=%s", authid); + snprintf(child_domain_arg, sizeof(child_domain_arg)-1, + "--domain=%s", domain); + + return fork_helper(&context->helper, "ntlm_auth", + argv, params->utils); +} + +static int gssspnego_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + struct gssspnego_context *context = + malloc(sizeof(struct gssspnego_context)); + + if (context == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + memset(context, 0, sizeof(struct gssspnego_context)); + + *conn_context = context; + + return SASL_OK; +} + +static int gssspnego_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + struct gssspnego_context *context = + (struct gssspnego_context *)conn_context; + + unsigned char childbuf[1025]; + unsigned childbuflen; + + int result; + + static sasl_secret_t *password; + static int must_free_password; + static int must_send_password = 0; + + if (context->helper == NULL) { + result = gssspnego_new_client_helper(context, params, + prompt_need, oparams); + + if (result != SASL_OK) + return result; + } + + if (must_send_password != 0) { + + must_send_password = 0; + + send_password: + + strncpy(childbuf, "PW ", sizeof(childbuf)-1); + + if (params->utils->encode64(password->data, password->len, + childbuf+3, sizeof(childbuf)-3, + &childbuflen) != SASL_OK) { + syslog(LOG_DEBUG, + "Could not convert password to base64\n"); + return SASL_FAIL; + } + + if ((result = interact_helper(context->helper, + childbuf, + sizeof(childbuf), + &childbuflen)) != SASL_OK) + return result; + + if (strncmp(childbuf, "OK", 2) != 0) { + syslog(LOG_DEBUG, "Child did not accept our password\n"); + return SASL_FAIL; + } + } + + strncpy(childbuf, "TT ", sizeof(childbuf)-1); + + if (params->utils->encode64(serverin, serverinlen, + childbuf+3, sizeof(childbuf)-4, + &childbuflen) != SASL_OK) { + syslog(LOG_DEBUG, + "Could not convert serverin to base64\n"); + return SASL_FAIL; + } + + if ((result = interact_helper(context->helper, + childbuf, + sizeof(childbuf), + &childbuflen)) != SASL_OK) + return result; + + /* The child's reply can be: + "PW" -> It wants a password + "KK " -> Send to the server + "AF" -> Authenticated + "NA" -> Not Authenticated + */ + + if (strncmp(childbuf, "PW", 2) == 0) { + + result = _plug_get_password(params->utils, &password, + &must_free_password, prompt_need); + + if ( (result != SASL_OK) && + (result != SASL_INTERACT) ) + return result; + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + if (result == SASL_OK) + goto send_password; + + result = _plug_make_prompts(params->utils, prompt_need, + NULL, NULL, + NULL, NULL, + "Password", NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) + return result; + return SASL_INTERACT; + + } + + if (strncmp(childbuf, "KK ", 3) == 0) { + static char bin[2048]; + + if (params->utils->decode64(childbuf+3, childbuflen-3, + bin, sizeof(bin)-1, + clientoutlen) != SASL_OK) { + syslog(LOG_DEBUG, "Could not decode child's base64\n"); + return SASL_FAIL; + } + *clientout = bin; + return SASL_CONTINUE; + } + + if ( (strncmp(childbuf, "AF", 2) == 0) || + (strncmp(childbuf, "NA", 2) == 0) ) { + + kill_helper(context->helper); + + *clientout = NULL; + *clientoutlen = 0; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + + return (strncmp(childbuf, "AF", 2) == 0) ? + SASL_OK : SASL_BADAUTH; + } + + return SASL_FAIL; +} + +static sasl_client_plug_t gssspnego_client_plugins[] = +{ + { + "GSS-SPNEGO", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT, /* security_flags */ + SASL_FEAT_SERVER_FIRST, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &gssspnego_client_mech_new, /* mech_new */ + &gssspnego_client_mech_step, /* mech_step */ + NULL, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int gssspnego_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR(utils, "GSSSPNEGO version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = gssspnego_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff -urN cyrus-sasl-2.1.20.orig/plugins/makeinit.sh cyrus-sasl-2.1.20/plugins/makeinit.sh --- cyrus-sasl-2.1.20.orig/plugins/makeinit.sh 2003-07-15 18:38:59.000000000 +0100 +++ cyrus-sasl-2.1.20/plugins/makeinit.sh 2005-02-09 08:00:56.770816328 +0000 @@ -1,4 +1,4 @@ -for mech in anonymous crammd5 digestmd5 gssapiv2 kerberos4 login ntlm otp plain srp; do +for mech in anonymous crammd5 digestmd5 gssapiv2 kerberos4 login ntlm gssspnego otp plain srp; do echo " #include diff -urN cyrus-sasl-2.1.20.orig/plugins/ntlm_samba.c cyrus-sasl-2.1.20/plugins/ntlm_samba.c --- cyrus-sasl-2.1.20.orig/plugins/ntlm_samba.c 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-sasl-2.1.20/plugins/ntlm_samba.c 2005-02-09 08:00:56.771816176 +0000 @@ -0,0 +1,561 @@ +/* NTLM (via ntlm_auth) plugin + * Volker Lendecke, Andrew Bartlett + */ +/* + * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "plugin_common.h" +#include "smb_helper.h" + +#ifdef macintosh +#include +#endif + +/***************************** Common Section *****************************/ + +static const char plugin_id[] = "$Id: ntlm.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $"; + +/***************************** Server Section *****************************/ + +struct ntlm_context { + struct smb_helper *helper; + int first; + sasl_secret_t *password; /* user password */ + unsigned int free_password; /* set if we need to free password */ + unsigned int sent_password; /* set if we have told ntlm_auth the password already */ + const char *authid; + const char *domain; +}; + +static int ntlm_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + char * argv[] = { "ntlm_auth", + "--helper-protocol=squid-2.5-ntlmssp", + NULL }; + + struct ntlm_context *context = + malloc(sizeof(struct ntlm_context)); + + if (context == NULL) { + syslog(LOG_DEBUG, "ntlm_server_mech_new: No memory\n"); + MEMERROR(sparams->utils); + return SASL_NOMEM; + } + + memset(context, 0, sizeof(struct ntlm_context)); + context->first = 1; + + *conn_context = context; + + return fork_helper(&context->helper, "ntlm_auth", + argv, sparams->utils); +} + +static int ntlm_server_mech_step(void *conn_context, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + struct ntlm_context *context = + (struct ntlm_context *)conn_context; + + unsigned char childbuf[1025]; + unsigned childbuflen; + + unsigned base64len; + + static unsigned char bin[1025]; + + int result; + + if (context->first) { + strncpy(childbuf, "YR ", sizeof(childbuf)-1); + context->first = 0; + } else { + strncpy(childbuf, "KK ", sizeof(childbuf)-1); + } + + if (params->utils->encode64(clientin, clientinlen, + childbuf+3, sizeof(childbuf)-4, + &childbuflen) != SASL_OK) { + syslog(LOG_DEBUG, + "Could not convert clientin to base64\n"); + return SASL_FAIL; + } + + if ((result = interact_helper(context->helper, + childbuf, + sizeof(childbuf), + &childbuflen)) != SASL_OK) + return result; + + /* The child's reply contains 3 parts: + - The code: TT, AF or NA + - The blob to send to the client, coded in base64 + - or for AF it's domain\\user + for NA it's the NT error code + */ + + if (strlen(childbuf) < 3) { + syslog(LOG_DEBUG, "Got invalid response from child (parse of ntlm_auth output failure)\n"); + return SASL_FAIL; + } + + base64len = strlen(childbuf) - 3; + + if ( strncmp(childbuf, "TT ", 3) == 0) { + if (params->utils->decode64(childbuf+3, base64len, + bin, sizeof(bin)-1, + serveroutlen) != SASL_OK) { + syslog(LOG_DEBUG, "Could not decode child's base64\n"); + return SASL_FAIL; + } + *serverout = bin; + + /* Next round */ + return SASL_CONTINUE; + } + if (strncmp(childbuf, "NA ", 3) == 0) { + /* Not Authenticated */ + kill_helper(context->helper); + return SASL_BADAUTH; + } + + if (strncmp(childbuf, "AF ", 3) == 0) { + /* Authentication Fine */ + char *user = childbuf + 3; + int status; + + kill_helper(context->helper); + + if ( (status = params->canon_user(params->utils->conn, + user, 0, SASL_CU_AUTHID, + oparams)) != SASL_OK ) { + syslog(LOG_DEBUG, "canon_user for AUTHID [%s] failed\n", user); + return status; + } + + if ( (status = params->canon_user(params->utils->conn, + user, 0, SASL_CU_AUTHZID, + oparams)) != SASL_OK ) { + syslog(LOG_DEBUG, "canon_user for AUTHZID [%s] failed\n", user); + return status; + } + + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + + return SASL_OK; + } + + syslog(LOG_DEBUG, "Child's response unknown\n"); + return SASL_FAIL; +} + +static void ntlm_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils __attribute__((unused))) +{ + struct ntlm_context *context = + (struct ntlm_context *)conn_context; + + kill_helper(context->helper); + + return; +} + +static sasl_server_plug_t ntlm_server_plugins[] = +{ + { + "NTLM", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS + | SASL_SEC_NOPLAINTEXT, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST, /* features */ + NULL, /* glob_context */ + &ntlm_server_mech_new, /* mech_new */ + &ntlm_server_mech_step, /* mech_step */ + &ntlm_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech_avail */ + NULL /* spare */ + } +}; + +int ntlm_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR(utils, "NTLM version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = ntlm_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +static int ntlm_new_client_helper(struct ntlm_context *context, + sasl_client_params_t *params, + sasl_interact_t **prompt_need, + sasl_out_params_t *oparams) +{ + int auth_result = SASL_OK; + int domain_result = SASL_OK; + + int pass_result = SASL_OK; + int result; + + char child_user_arg[512]; + char child_domain_arg[512]; + + const char *domains[] = { + "", + NULL + }; + + char *argv[] = { "ntlm_auth", + "--helper-protocol=ntlmssp-client-1", + child_user_arg, + child_domain_arg, + NULL }; + + if (context->authid == NULL) { + auth_result = _plug_get_authid(params->utils, + &context->authid, prompt_need); + + if ( (auth_result != SASL_OK) && + (auth_result != SASL_INTERACT) && + (context->authid == NULL) ) + return auth_result; + } + + if (context->domain == NULL) { + domain_result = _plug_get_realm(params->utils, domains, + &context->domain, prompt_need); + if ( (domain_result != SASL_OK) && + (domain_result != SASL_INTERACT) && + (context->domain == NULL) ) + return domain_result; + } + + if (context->password == NULL) { + pass_result = _plug_get_password(params->utils, &context->password, + &context->free_password, prompt_need); + + if ( (pass_result != SASL_OK) && + (pass_result != SASL_INTERACT) + && (context->password == NULL)) + return pass_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + if ( (auth_result == SASL_INTERACT) || + (domain_result == SASL_INTERACT) ) { + result = _plug_make_prompts(params->utils, prompt_need, + NULL, NULL, + (auth_result == SASL_INTERACT) ? + "Username" : NULL, NULL, + pass_result == SASL_INTERACT ? + "Password" : NULL, NULL, + NULL, NULL, NULL, + NULL, + (domain_result == SASL_INTERACT) ? + "Realm (Domain)" : NULL, NULL); + + if (result != SASL_OK) + return result; + return SASL_INTERACT; + } + + if (!context->password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + oparams->user = strdup(context->authid); + oparams->ulen = strlen(context->authid); + oparams->authid = strdup(context->authid); + oparams->alen = oparams->ulen; + + snprintf(child_user_arg, sizeof(child_user_arg)-1, + "--username=%s", context->authid); + snprintf(child_domain_arg, sizeof(child_domain_arg)-1, + "--domain=%s", context->domain); + + return fork_helper(&context->helper, "ntlm_auth", + argv, params->utils); +} + +static int ntlm_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + struct ntlm_context *context = + malloc(sizeof(struct ntlm_context)); + + if (context == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + memset(context, 0, sizeof(struct ntlm_context)); + context->first = 1; + + *conn_context = context; + + return SASL_OK; +} + +static int ntlm_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + struct ntlm_context *context = + (struct ntlm_context *)conn_context; + + unsigned char childbuf[1025]; + unsigned childbuflen; + + int result; + + if (context->helper == NULL) { + result = ntlm_new_client_helper(context, params, + prompt_need, oparams); + + if (result != SASL_OK) + return result; + } + + if (!context->sent_password) { + + strncpy(childbuf, "PW ", sizeof(childbuf)-1); + + if (params->utils->encode64(context->password->data, context->password->len, + childbuf+3, sizeof(childbuf)-3, + &childbuflen) != SASL_OK) { + syslog(LOG_DEBUG, + "Could not convert password to base64\n"); + return SASL_FAIL; + } + + if ((result = interact_helper(context->helper, + childbuf, + sizeof(childbuf), + &childbuflen)) != SASL_OK) + return result; + + if (strncmp(childbuf, "OK", 2) != 0) { + syslog(LOG_DEBUG, "Child did not accept our password\n"); + return SASL_FAIL; + } + context->sent_password = 1; + } + + if (context->first) { + strncpy(childbuf, "YR", sizeof(childbuf)-1); + context->first = 0; + } else { + strncpy(childbuf, "TT", sizeof(childbuf)-1); + } + + if (serverinlen) { + strncpy(childbuf+2, " ", sizeof(childbuf)-3); + if (params->utils->encode64(serverin, serverinlen, + childbuf+3, sizeof(childbuf)-4, + &childbuflen) != SASL_OK) { + syslog(LOG_DEBUG, + "Could not convert serverin to base64\n"); + return SASL_FAIL; + } + } + + if ((result = interact_helper(context->helper, + childbuf, + sizeof(childbuf), + &childbuflen)) != SASL_OK) + return result; + + /* The child's reply can be: + "PW" -> It wants a password (but we premptivly deal with that above) + "YR " -> Send (initial) to the server + "KK " -> Send to the server + "AF" -> Authenticated + "NA" -> Not Authenticated + */ + + if ((strncmp(childbuf, "YR ", 3) == 0) || (strncmp(childbuf, "KK ", 3) == 0)) { + static char bin[2048]; + + if (params->utils->decode64(childbuf+3, childbuflen-3, + bin, sizeof(bin)-1, + clientoutlen) != SASL_OK) { + syslog(LOG_DEBUG, "Could not decode child's base64\n"); + return SASL_FAIL; + } + *clientout = bin; + return SASL_CONTINUE; + } + + if ( (strncmp(childbuf, "AF", 2) == 0) || + (strncmp(childbuf, "NA", 2) == 0) ) { + + kill_helper(context->helper); + + *clientout = NULL; + *clientoutlen = 0; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + + return (strncmp(childbuf, "AF", 2) == 0) ? + SASL_OK : SASL_BADAUTH; + } + + return SASL_FAIL; +} + +static void ntlm_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils __attribute__((unused))) +{ + struct ntlm_context *context = + (struct ntlm_context *)conn_context; + + kill_helper(context->helper); + + return; +} + +static void ntlm_client_mech_free(void *conn_context, + const sasl_utils_t *utils __attribute__((unused))) +{ + struct ntlm_context *context = + (struct ntlm_context *)conn_context; + + if (context->free_password) + _plug_free_secret(utils, &context->password); + + return; +} + +static sasl_client_plug_t ntlm_client_plugins[] = +{ + { + "NTLM", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &ntlm_client_mech_new, /* mech_new */ + &ntlm_client_mech_step, /* mech_step */ + &ntlm_client_mech_dispose, /* mech_dispose */ + &ntlm_client_mech_free, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int ntlm_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR(utils, "NTLM version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = ntlm_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff -urN cyrus-sasl-2.1.20.orig/plugins/smb_helper.c cyrus-sasl-2.1.20/plugins/smb_helper.c --- cyrus-sasl-2.1.20.orig/plugins/smb_helper.c 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-sasl-2.1.20/plugins/smb_helper.c 2005-02-09 08:00:56.771816176 +0000 @@ -0,0 +1,168 @@ +/* Interface functions to samba's ntlm_auth helper + * Volker Lendecke, Andrew Bartlett + * + * $Id:$ + */ +/* + * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "plugin_common.h" +#include "smb_helper.h" + +int fork_helper(struct smb_helper **new_helper, + const char *prog, + char * const argv[], + const sasl_utils_t *utils) +{ + int pipe_in[2]; + int pipe_out[2]; + + struct smb_helper *helper = malloc(sizeof(struct smb_helper)); + + if (helper == NULL) { + syslog(LOG_DEBUG, "fork_helper: No memory\n"); + MEMERROR(utils); + return SASL_NOMEM; + } + + *new_helper = helper; + + if ( (pipe(pipe_in) < 0) || (pipe(pipe_out) < 0) ) { + syslog(LOG_DEBUG, "fork_helper: could not open pipes\n"); + utils->seterror(utils->conn, 0, "Could not allocate pipe\n"); + return SASL_FAIL; + } + + helper->child_pid = fork(); + + if (helper->child_pid == -1) { + syslog(LOG_DEBUG, "fork_helper: Could not fork\n"); + utils->seterror(utils->conn, 0, "Could not fork\n"); + return SASL_FAIL; + } + + if (helper->child_pid == 0) { + + /* Set up the pipes correctly */ + + close(0); + close(1); + dup2(pipe_out[0], 0); + close(pipe_out[0]); + close(pipe_out[1]); + dup2(pipe_in[1], 1); + close(pipe_in[0]); + close(pipe_in[1]); + + execvp(prog, argv); + + /* We should never get here ... */ + exit(1); + + } else { + + helper->pipe_in = fdopen(pipe_in[0], "r"); + close(pipe_in[1]); + helper->pipe_out = fdopen(pipe_out[1], "w"); + close(pipe_out[0]); + } + + return SASL_OK; +} + +int interact_helper(struct smb_helper *helper, + unsigned char *childbuf, + unsigned max_childbuflen, + unsigned *childbuflen) +{ + syslog(LOG_DEBUG, "interact_helper: Sending %d bytes do child: %s\n", + *childbuflen, childbuf); + + fprintf(helper->pipe_out, "%s\n", childbuf); + fflush(helper->pipe_out); + + if (fgets(childbuf, max_childbuflen-1, helper->pipe_in) == NULL) { + syslog(LOG_DEBUG, "interact_helper: Did not get a response from helper\n"); + return SASL_FAIL; + } + + *childbuflen = strlen(childbuf)-1; + childbuf[*childbuflen] = '\0'; /* Strip trailing \n */ + + syslog(LOG_DEBUG, "interact_helper: Got %d bytes from helper: %s\n", + *childbuflen, childbuf); + + if ( *childbuflen < 2 ) { + syslog(LOG_DEBUG, "interact_helper: Got invalid response from helper\n"); + return SASL_FAIL; + } + + if ((*childbuflen <= 3) && (strncmp(childbuf, "BH", 2) == 0)) { + syslog(LOG_DEBUG, "interact_helper: Broke Helper somehow...\n"); + return SASL_FAIL; + } + return SASL_OK; +} + +void kill_helper(struct smb_helper *helper) +{ + int status; + + if ((helper == NULL) || (helper->child_pid == 0)) + return; + + fclose(helper->pipe_out); + fclose(helper->pipe_in); + + waitpid(helper->child_pid, &status, 0); + syslog(LOG_DEBUG, "kill_helper: Helper died with status %d\n", status); + + helper->child_pid = 0; + free(helper); +} diff -urN cyrus-sasl-2.1.20.orig/plugins/smb_helper.h cyrus-sasl-2.1.20/plugins/smb_helper.h --- cyrus-sasl-2.1.20.orig/plugins/smb_helper.h 1970-01-01 01:00:00.000000000 +0100 +++ cyrus-sasl-2.1.20/plugins/smb_helper.h 2005-02-09 08:00:56.771816176 +0000 @@ -0,0 +1,73 @@ +/* Interface functions to samba's ntlm_auth helper + * Volker Lendecke, Andrew Bartlett + * + * $Id:$ + */ +/* + * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SMB_HELPER_H_ +#define _SMB_HELPER_H_ + +#include +#include + +#include +#include + +struct smb_helper { + pid_t child_pid; + FILE *pipe_in; + FILE *pipe_out; +}; + +int fork_helper(struct smb_helper **helper, + const char *prog, + char * const argv[], + const sasl_utils_t *utils); + +int interact_helper(struct smb_helper *helper, + unsigned char *childbuf, + unsigned max_childbuflen, + unsigned *childbuflen); + +void kill_helper(struct smb_helper *helper); + +#endif