[Dovecot] [PATCH] Support for GSSAPI SASL Mechanism
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi, Attached is a patch against current CVS that adds support for the GSSAPI SASL mechanism. It was written from scratch, after reading the patch from Colin Walters against a much older version of dovecot. Other then support for the 'GSSAPI' mechanism, it contains the following changes: - - Added 'auth_krb5_keytab' option for overriding default keytab location. The gssapi library already uses the environment variable KRB5_KTNAME to allow overriding of the keytab location, so I didn't have to make any changes on the login process side. - - Added 'need_passdb' member to mech_module and allow having no passdb's specified if no mechanisms need them (only useful for GSSAPI at the moment) Cheers, Jelmer - -- Jelmer Vernooij <jelmer@samba.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2 (GNU/Linux) iD8DBQFDVgaEPa9Uoh7vUnYRAscGAJ9Fa4E5ze5IX3UeAfjwg7kzj5tgtQCcDva7 O5VTeOeJ7zpmHullxQN6q1Y= =60w7 -----END PGP SIGNATURE----- Index: configure.in =================================================================== RCS file: /home/cvs/dovecot/configure.in,v retrieving revision 1.214 diff -u -r1.214 configure.in --- configure.in 23 Sep 2005 13:28:40 -0000 1.214 +++ configure.in 19 Oct 2005 08:25:31 -0000 @@ -118,6 +118,15 @@ fi, want_bsdauth=yes) +AC_ARG_WITH(gssapi, +[ --with-gssapi Build with GSSAPI authentication support (default)], + if test x$withval = xno; then + want_gssapi=no + else + want_gssapi_yes + fi, + want_gssapi=yes) + AC_ARG_WITH(ldap, [ --with-ldap Build with LDAP support], if test x$withval = xno; then @@ -1192,6 +1201,17 @@ ]) fi +have_gssapi=no +if test $want_gssapi = yes; then + AC_CHECK_PROG(KRB5CONFIG, krb5-config, YES, NO) + if test $KRB5CONFIG = YES; then + AUTH_LIBS="$AUTH_LIBS `krb5-config --libs gssapi`" + AUTH_CFLAGS="$AUTH_CFLAGS `krb5-config --cflags gssapi`" + AC_DEFINE(MECH_GSSAPI,, Build with GSSAPI support) + have_gssapi=yes + fi +fi + if test $want_ldap = yes; then AC_CHECK_LIB(ldap, ldap_init, [ AC_CHECK_HEADER(ldap.h, [ @@ -1486,5 +1506,6 @@ echo "Building with IPv6 support .......... : $want_ipv6" echo "Building with pop3 server ........... : $want_pop3d" echo "Building with mail delivery agent .. : $want_deliver" +echo "Building with GSSAPI support ........ : $have_gssapi" echo "Building with user database modules . :$userdb" echo "Building with password lookup modules :$passdb" Index: dovecot-example.conf =================================================================== RCS file: /home/cvs/dovecot/dovecot-example.conf,v retrieving revision 1.155 diff -u -r1.155 dovecot-example.conf --- dovecot-example.conf 16 Oct 2005 14:59:12 -0000 1.155 +++ dovecot-example.conf 19 Oct 2005 08:25:31 -0000 @@ -568,9 +568,13 @@ # automatically created and destroyed as needed. #auth_worker_max_count = 30 +# Kerberos keytab to use for the GSSAPI mechanism. Will use the system +# default (usually /etc/krb5.keytab) if not specified. +#auth_krb5_keytab = + auth default { # Space separated list of wanted authentication mechanisms: - # plain digest-md5 cram-md5 apop anonymous + # plain digest-md5 cram-md5 apop anonymous gssapi mechanisms = plain # Index: src/auth/Makefile.am =================================================================== RCS file: /home/cvs/dovecot/src/auth/Makefile.am,v retrieving revision 1.47 diff -u -r1.47 Makefile.am --- src/auth/Makefile.am 7 Aug 2005 11:41:19 -0000 1.47 +++ src/auth/Makefile.am 19 Oct 2005 08:25:31 -0000 @@ -54,6 +54,7 @@ mech-cram-md5.c \ mech-digest-md5.c \ mech-ntlm.c \ + mech-gssapi.c \ mech-rpa.c \ mech-apop.c \ passdb.c \ Index: src/auth/auth.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/auth.c,v retrieving revision 1.19 diff -u -r1.19 auth.c --- src/auth/auth.c 16 Oct 2005 14:06:59 -0000 1.19 +++ src/auth/auth.c 19 Oct 2005 08:25:31 -0000 @@ -56,8 +56,6 @@ } t_pop(); - if (auth->passdbs == NULL) - i_fatal("No password databases set"); if (auth->userdbs == NULL) i_fatal("No user databases set"); return auth; @@ -130,6 +128,9 @@ struct mech_module_list *list; for (list = auth->mech_modules; list != NULL; list = list->next) { + if (list->module.need_passdb && + !auth->passdbs) + break; if (list->module.passdb_need_plain && !auth_passdb_list_have_plain(auth)) break; Index: src/auth/mech-anonymous.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-anonymous.c,v retrieving revision 1.13 diff -u -r1.13 mech-anonymous.c --- src/auth/mech-anonymous.c 23 Apr 2005 09:11:49 -0000 1.13 +++ src/auth/mech-anonymous.c 19 Oct 2005 08:25:31 -0000 @@ -57,6 +57,7 @@ MEMBER(flags) MECH_SEC_ANONYMOUS, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) FALSE, Index: src/auth/mech-apop.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-apop.c,v retrieving revision 1.16 diff -u -r1.16 mech-apop.c --- src/auth/mech-apop.c 23 Apr 2005 09:11:49 -0000 1.16 +++ src/auth/mech-apop.c 19 Oct 2005 08:25:31 -0000 @@ -162,6 +162,7 @@ MEMBER(flags) MECH_SEC_PRIVATE | MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, Index: src/auth/mech-cram-md5.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-cram-md5.c,v retrieving revision 1.20 diff -u -r1.20 mech-cram-md5.c --- src/auth/mech-cram-md5.c 8 Jan 2005 21:37:32 -0000 1.20 +++ src/auth/mech-cram-md5.c 19 Oct 2005 08:25:32 -0000 @@ -191,6 +191,7 @@ MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, Index: src/auth/mech-digest-md5.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-digest-md5.c,v retrieving revision 1.34 diff -u -r1.34 mech-digest-md5.c --- src/auth/mech-digest-md5.c 8 Jan 2005 21:37:32 -0000 1.34 +++ src/auth/mech-digest-md5.c 19 Oct 2005 08:25:32 -0000 @@ -619,6 +619,7 @@ MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE | MECH_SEC_MUTUAL_AUTH, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, Index: src/auth/mech-login.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-login.c,v retrieving revision 1.12 diff -u -r1.12 mech-login.c --- src/auth/mech-login.c 23 Apr 2005 09:11:49 -0000 1.12 +++ src/auth/mech-login.c 19 Oct 2005 08:25:32 -0000 @@ -87,6 +87,7 @@ MEMBER(flags) MECH_SEC_PLAINTEXT, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) TRUE, MEMBER(passdb_need_credentials) FALSE, Index: src/auth/mech-ntlm.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-ntlm.c,v retrieving revision 1.19 diff -u -r1.19 mech-ntlm.c --- src/auth/mech-ntlm.c 23 Apr 2005 09:11:49 -0000 1.19 +++ src/auth/mech-ntlm.c 19 Oct 2005 08:25:32 -0000 @@ -281,6 +281,7 @@ MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, Index: src/auth/mech-plain.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-plain.c,v retrieving revision 1.31 diff -u -r1.31 mech-plain.c --- src/auth/mech-plain.c 23 Apr 2005 09:11:49 -0000 1.31 +++ src/auth/mech-plain.c 19 Oct 2005 08:25:32 -0000 @@ -103,6 +103,7 @@ MEMBER(flags) MECH_SEC_PLAINTEXT, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) TRUE, MEMBER(passdb_need_credentials) FALSE, Index: src/auth/mech-rpa.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech-rpa.c,v retrieving revision 1.19 diff -u -r1.19 mech-rpa.c --- src/auth/mech-rpa.c 23 Apr 2005 09:11:49 -0000 1.19 +++ src/auth/mech-rpa.c 19 Oct 2005 08:25:32 -0000 @@ -614,6 +614,7 @@ MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE | MECH_SEC_MUTUAL_AUTH, + MEMBER(need_passdb) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, Index: src/auth/mech.c =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech.c,v retrieving revision 1.54 diff -u -r1.54 mech.c --- src/auth/mech.c 7 Jan 2005 19:55:50 -0000 1.54 +++ src/auth/mech.c 19 Oct 2005 08:25:32 -0000 @@ -54,6 +54,9 @@ extern struct mech_module mech_ntlm; extern struct mech_module mech_rpa; extern struct mech_module mech_anonymous; +#ifdef MECH_GSSAPI +extern struct mech_module mech_gssapi; +#endif void mech_init(void) { @@ -65,6 +68,9 @@ mech_register_module(&mech_ntlm); mech_register_module(&mech_rpa); mech_register_module(&mech_anonymous); +#ifdef MECH_GSSAPI + mech_register_module(&mech_gssapi); +#endif } void mech_deinit(void) @@ -77,4 +83,7 @@ mech_unregister_module(&mech_ntlm); mech_unregister_module(&mech_rpa); mech_unregister_module(&mech_anonymous); +#ifdef MECH_GSSAPI + mech_unregister_module(&mech_gssapi); +#endif } Index: src/auth/mech.h =================================================================== RCS file: /home/cvs/dovecot/src/auth/mech.h,v retrieving revision 1.35 diff -u -r1.35 mech.h --- src/auth/mech.h 9 Jan 2005 16:54:48 -0000 1.35 +++ src/auth/mech.h 19 Oct 2005 08:25:32 -0000 @@ -24,6 +24,7 @@ const char *mech_name; enum mech_security_flags flags; + unsigned int need_passdb:1; unsigned int passdb_need_plain:1; unsigned int passdb_need_credentials:1; Index: src/master/auth-process.c =================================================================== RCS file: /home/cvs/dovecot/src/master/auth-process.c,v retrieving revision 1.82 diff -u -r1.82 auth-process.c --- src/master/auth-process.c 1 Oct 2005 10:52:16 -0000 1.82 +++ src/master/auth-process.c 19 Oct 2005 08:25:33 -0000 @@ -458,6 +458,8 @@ env_put("SSL_REQUIRE_CLIENT_CERT=1"); if (set->ssl_username_from_cert) env_put("SSL_USERNAME_FROM_CERT=1"); + if (set->krb5_keytab) + env_put(t_strconcat("KRB5_KTNAME=", set->krb5_keytab, NULL)); restrict_process_size(set->process_size, (unsigned int)-1); } Index: src/master/master-settings.c =================================================================== RCS file: /home/cvs/dovecot/src/master/master-settings.c,v retrieving revision 1.92 diff -u -r1.92 master-settings.c --- src/master/master-settings.c 16 Oct 2005 14:59:17 -0000 1.92 +++ src/master/master-settings.c 19 Oct 2005 08:25:33 -0000 @@ -155,6 +155,7 @@ DEF(SET_STR, username_chars), DEF(SET_STR, username_translation), DEF(SET_STR, anonymous_username), + DEF(SET_STR, krb5_keytab), DEF(SET_BOOL, verbose), DEF(SET_BOOL, debug), @@ -353,6 +354,7 @@ MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@", MEMBER(username_translation) "", MEMBER(anonymous_username) "anonymous", + MEMBER(krb5_keytab) NULL, MEMBER(verbose) FALSE, MEMBER(debug) FALSE, Index: src/master/master-settings.h =================================================================== RCS file: /home/cvs/dovecot/src/master/master-settings.h,v retrieving revision 1.60 diff -u -r1.60 master-settings.h --- src/master/master-settings.h 1 Oct 2005 10:52:16 -0000 1.60 +++ src/master/master-settings.h 19 Oct 2005 08:25:33 -0000 @@ -162,6 +162,7 @@ const char *username_chars; const char *username_translation; const char *anonymous_username; + const char *krb5_keytab; int verbose, debug; int ssl_require_client_cert; --- /dev/null 2005-10-17 12:49:06.896857000 +0200 +++ src/auth/mech-gssapi.c 2005-10-19 10:17:45.000000000 +0200 @@ -0,0 +1,321 @@ +/* + * GSSAPI Module + * + * Copyright (c) 2005 Jelmer Vernooij <jelmer@samba.org> + * + * Related standards: + * - draft-ietf-sasl-gssapi-03 + * - RFC2222 + * + * Some parts inspired by an older patch from Colin Walters + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "common.h" +#include "mech.h" +#include "passdb.h" +#include "str.h" +#include "buffer.h" +#include "hex-binary.h" +#include "safe-memset.h" +#include "hostpid.h" + +#ifdef MECH_GSSAPI + +#include <gssapi/gssapi.h> + +struct gssapi_auth_request { + struct auth_request auth_request; + gss_ctx_id_t gss_ctx; + gss_cred_id_t service_cred; + + enum { + GSS_STATE_SEC_CONTEXT, + GSS_STATE_WRAP, + GSS_STATE_UNWRAP + } sasl_gssapi_state; + + gss_name_t src_name; + + pool_t pool; +}; + +static void auth_request_log_gss_error(struct auth_request *request, OM_uint32 status_value, int status_type, const char *description) +{ + OM_uint32 message_context = 0; + OM_uint32 major_status, minor_status; + gss_buffer_desc status_string; + + do { + major_status = gss_display_status(&minor_status, status_value, + status_type, GSS_C_NO_OID, &message_context, + &status_string); + + auth_request_log_error(request, "gssapi", "While %s: %s", + description, (char *)status_string.value); + + major_status = gss_release_buffer(&minor_status, &status_string); + } while (message_context != 0); +} + +static struct auth_request *mech_gssapi_auth_new(void) +{ + struct gssapi_auth_request *request; + pool_t pool; + + pool = pool_alloconly_create("gssapi_auth_request", 512); + request = p_new(pool, struct gssapi_auth_request, 1); + request->pool = pool; + + request->gss_ctx = GSS_C_NO_CONTEXT; + + request->auth_request.pool = pool; + return &request->auth_request; +} + +static OM_uint32 obtain_service_credentials(struct auth_request *request, gss_cred_id_t *ret) +{ + OM_uint32 major_status, minor_status; + string_t *principal_name; + gss_buffer_desc inbuf; + gss_name_t gss_principal; + + principal_name = t_str_new(40); + str_append(principal_name, t_str_lcase(request->service)); + str_append(principal_name, "@"); + str_append(principal_name, my_hostname); + + auth_request_log_info(request, "gssapi", + "Obtaining credentials for %s", str_c(principal_name)); + + inbuf.length = str_len(principal_name); + inbuf.value = (void*) str_c(principal_name); + + major_status = gss_import_name(&minor_status, &inbuf, + GSS_C_NT_HOSTBASED_SERVICE, &gss_principal); + + str_free(principal_name); + + if (GSS_ERROR(major_status)) { + auth_request_log_gss_error(request, major_status, GSS_C_GSS_CODE, + "importing principal name"); + return major_status; + } + + major_status = gss_acquire_cred(&minor_status, gss_principal, 0, + GSS_C_NULL_OID_SET, GSS_C_ACCEPT, ret, NULL, NULL); + + if (GSS_ERROR(major_status)) { + auth_request_log_gss_error(request, major_status, GSS_C_GSS_CODE, + "acquiring service credentials"); + auth_request_log_gss_error(request, minor_status, GSS_C_MECH_CODE, + "acquiring service credentials"); + return major_status; + } + + gss_release_name(&minor_status, gss_principal); + + return major_status; +} + +static void gssapi_sec_context(struct gssapi_auth_request *request, + gss_buffer_desc inbuf) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc outbuf; + + major_status = gss_accept_sec_context ( + &minor_status, + &request->gss_ctx, + request->service_cred, + &inbuf, + GSS_C_NO_CHANNEL_BINDINGS, + &request->src_name, + NULL, /* mech_type */ + &outbuf, + NULL, /* ret_flags */ + NULL, /* time_rec */ + NULL /* delegated_cred_handle */ + ); + + if (GSS_ERROR(major_status)) { + auth_request_log_gss_error(&request->auth_request, major_status, GSS_C_GSS_CODE, + "processing incoming data"); + auth_request_log_gss_error(&request->auth_request, minor_status, GSS_C_MECH_CODE, + "processing incoming data"); + + auth_request_fail(&request->auth_request); + return; + } + + if (major_status == GSS_S_COMPLETE) { + request->sasl_gssapi_state = GSS_STATE_WRAP; + auth_request_log_info(&request->auth_request, "gssapi", + "security context state completed."); + } else { + auth_request_log_info(&request->auth_request, "gssapi", + "Processed incoming packet correctly, waiting for another."); + } + + request->auth_request.callback(&request->auth_request, + AUTH_CLIENT_RESULT_CONTINUE, + outbuf.value, outbuf.length); + + major_status = gss_release_buffer(&minor_status, &outbuf); +} + +static void gssapi_wrap(struct gssapi_auth_request *request, + gss_buffer_desc inbuf) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc outbuf; + char ret[4]; + + /* The clients return data should be empty here */ + + ret[0] = 0x01; /* Only authentication, no integrity or confidentiality protection (yet?) */ + ret[1] = 0xFF; + ret[2] = 0xFF; + ret[3] = 0xFF; + + inbuf.length = 4; + inbuf.value = ret; + + major_status = gss_wrap(&minor_status, request->gss_ctx, 0, + GSS_C_QOP_DEFAULT, + &inbuf, NULL, &outbuf); + + if (GSS_ERROR(major_status)) { + auth_request_log_gss_error(&request->auth_request, major_status, GSS_C_GSS_CODE, + "sending security layer negotiation"); + auth_request_log_gss_error(&request->auth_request, minor_status, GSS_C_MECH_CODE, + "sending security layer negotiation"); + auth_request_fail(&request->auth_request); + return; + } + + auth_request_log_info(&request->auth_request, "gssapi", + "Negotiated security layer"); + + request->auth_request.callback(&request->auth_request, + AUTH_CLIENT_RESULT_CONTINUE, + outbuf.value, outbuf.length); + + major_status = gss_release_buffer(&minor_status, &outbuf); + + request->sasl_gssapi_state = GSS_STATE_UNWRAP; +} + +static void gssapi_unwrap(struct gssapi_auth_request *request, + gss_buffer_desc inbuf) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc outbuf; + + major_status = gss_unwrap(&minor_status, request->gss_ctx, + &inbuf, &outbuf, NULL, NULL); + + if (GSS_ERROR(major_status)) { + auth_request_log_gss_error(&request->auth_request, major_status, GSS_C_GSS_CODE, + "final negotiation: gss_unwrap"); + auth_request_fail(&request->auth_request); + return; + } + + if (outbuf.length <= 4) { + auth_request_log_error(&request->auth_request, "gssapi", + "Invalid response length"); + auth_request_fail(&request->auth_request); + return; + } + + request->auth_request.user = p_strndup(request->auth_request.pool, + ((unsigned char*) outbuf.value)+4, + outbuf.length-4); + + auth_request_success(&request->auth_request, NULL, 0); +} + +static void +mech_gssapi_auth_continue(struct auth_request *request, + const unsigned char *data, size_t data_size) +{ + struct gssapi_auth_request *gssapi_request = + (struct gssapi_auth_request *)request; + gss_buffer_desc inbuf; + + inbuf.value = (char*) data; + inbuf.length = data_size; + + switch (gssapi_request->sasl_gssapi_state) { + case GSS_STATE_SEC_CONTEXT: gssapi_sec_context(gssapi_request, inbuf); break; + case GSS_STATE_WRAP: gssapi_wrap(gssapi_request, inbuf); break; + case GSS_STATE_UNWRAP: gssapi_unwrap(gssapi_request, inbuf); break; + } +} + +static void +mech_gssapi_auth_initial(struct auth_request *request, + const unsigned char *data, size_t data_size) +{ + OM_uint32 major_status; + struct gssapi_auth_request *gssapi_request = + (struct gssapi_auth_request *)request; + + major_status = obtain_service_credentials(request, &gssapi_request->service_cred); + + if (GSS_ERROR(major_status)) { + auth_request_internal_failure(request); + return; + } + gssapi_request->src_name = GSS_C_NO_NAME; + + gssapi_request->sasl_gssapi_state = GSS_STATE_SEC_CONTEXT; + + if (data_size == 0) { + /* The client should go first */ + request->callback(request, + AUTH_CLIENT_RESULT_CONTINUE, + NULL, 0); + } else { + mech_gssapi_auth_continue(request, data, data_size); + } +} + + +static void +mech_gssapi_auth_free(struct auth_request *request) +{ + OM_uint32 major_status, minor_status; + struct gssapi_auth_request *gssapi_request = + (struct gssapi_auth_request *)request; + + major_status = gss_delete_sec_context(&minor_status, + &gssapi_request->gss_ctx, + GSS_C_NO_BUFFER); + + major_status = gss_release_cred(&minor_status, &gssapi_request->service_cred); + + pool_unref(request->pool); +} + +const struct mech_module mech_gssapi = { + "GSSAPI", + + MEMBER(flags) 0, + + MEMBER(need_passdb) FALSE, + MEMBER(passdb_need_plain) FALSE, + MEMBER(passdb_need_credentials) FALSE, + + mech_gssapi_auth_new, + mech_gssapi_auth_initial, + mech_gssapi_auth_continue, + mech_gssapi_auth_free +}; + +#endif Index: AUTHORS =================================================================== RCS file: /home/cvs/dovecot/AUTHORS,v retrieving revision 1.11 diff -u -r1.11 AUTHORS --- AUTHORS 19 Aug 2004 03:56:01 -0000 1.11 +++ AUTHORS 19 Oct 2005 08:38:49 -0000 @@ -12,6 +12,8 @@ Joshua Goodall <joshua@roughtrade.net> (src/auth/password-scheme-cram-md5.c, src/util/dovecotpw.c) +Jelmer Vernooij <jelmer@samba.org> (src/auth/mech-gssapi.c) + This product includes software developed by Computing Services at Carnegie Mellon University (http://www.cmu.edu/computing/). (src/lib/base64.c, src/lib/mkgmtime.c)
"JV" == Jelmer Vernooij <jelmer@samba.org> writes:
JV> Attached is a patch against current CVS that adds support for the
JV> GSSAPI SASL mechanism. It was written from scratch, after reading
JV> the patch from Colin Walters against a much older version of
JV> dovecot.
I too have been working on getting a working GSSAPI patch against current CVS and have taken a similar approach.
Any idea if this is going to make it's way into CVS?
I notice that its auth only and you don't have any SASL security layer integrity or protection stuff, same as DIGEST-MD5. This is the point which I've got to and have been considering how to implement the 'integrity-proxy' (name coined from the Colin Walters patch) part of things. Work on this would have implications for mech-digest-md5.c as well.
Want to discuss ideas?
Timo, do you have any ideas on a good way to implement this?
I have been considering:
- start up two pipe connected processes, a network filter and libexec/dovecot/imap, the filter does the gss_wrap, gss_unwrap etc
- create a io library filter layer
- keep the imap-login process around but have it re-exec as the filter (would be running as login_user, probably not ideal)
On Wed, Oct 19, 2005 at 02:11:14PM +0100, pod@herald.ox.ac.uk wrote about 'Re: [Dovecot] [PATCH] Support for GSSAPI SASL Mechanism':
"JV" == Jelmer Vernooij <jelmer@samba.org> writes: JV> Attached is a patch against current CVS that adds support for the JV> GSSAPI SASL mechanism. It was written from scratch, after reading JV> the patch from Colin Walters against a much older version of JV> dovecot. I too have been working on getting a working GSSAPI patch against current CVS and have taken a similar approach. Yeah, Timo mentioned there was someone else working on a new patch when I first talked to him yesterday evening, but I already had everything working by then, except for the configuration options, so I decided to go ahead. What's the status of your patch?
Any idea if this is going to make it's way into CVS? I hope so :-)
I notice that its auth only and you don't have any SASL security layer integrity or protection stuff, same as DIGEST-MD5.
I've focussed on authentication only for now since it keeps the patch small and readable (and thus is hopefully more easily accepted into CVS). Just authentication is sufficient for a lot of people.
Integrity and protection support can always be added later on by adding a couple of functions in mech-gssapi.c (and the right infrastructure in dovecot).
This is the point which I've got to and have been considering how to implement the 'integrity-proxy' (name coined from the Colin Walters patch) part of things. Work on this would have implications for mech-digest-md5.c as well. Yeah, that's a tricky. Especially when doing it in a way that has to be usable for more mechanisms.
Want to discuss ideas?
I have been considering:
- start up two pipe connected processes, a network filter and libexec/dovecot/imap, the filter does the gss_wrap, gss_unwrap etc
- create a io library filter layer This sounds like the sanest way of handling this to me. You'd have to export the GSS security context from the login process to the user process somehow, but that shouldn't be a problem with gss_{ex,im}port_sec_context().
- keep the imap-login process around but have it re-exec as the filter (would be running as login_user, probably not ideal)
Cheers,
Jelmer
On Wed, 2005-10-19 at 15:45 +0200, Jelmer Vernooij wrote:
Want to discuss ideas?
I have been considering:
- start up two pipe connected processes, a network filter and libexec/dovecot/imap, the filter does the gss_wrap, gss_unwrap etc
- create a io library filter layer This sounds like the sanest way of handling this to me. You'd have to export the GSS security context from the login process to the user process somehow, but that shouldn't be a problem with gss_{ex,im}port_sec_context().
If I understand this correctly, the whole thing begins from dovecot-auth process. From there on the security context is exported to login process as an authentication reply. Is it small enough that the 8192 byte input buffer is always enough?
Since at that point user has logged in, there's no need for login process to actually do anything else with the security context than get it passed onto imap process. Or this could be done by dovecot-auth -> master -> imap route using USER reply. Maybe better that way. In any case login/master processes wouldn't have to know anything about GSSAPI there, they'd just forward parameters from dovecot-auth blindly (I think master already does?).
Or it could be done in login process as a proxy the same way as SSL is currently handled. Login process is a bit hacky anyway so having GSSAPI proxy there wouldn't make it much worse :) Also the plus side there is that if there happens to be any exploitable security holes in GSSAPI library, login process would be running in chroot and attacker wouldn't get direct access to anyone's mails.
Doing it in imap/pop3 process would allow it to be done using a plugin though.. The "plugin" could be statically linked, but it could still be fully isolated from the imap/pop3 code itself. configure script could just generate some imap-plugins.c which contains gssapi_plugin_init() call or something after which it does all the rest internally. Possibly requires some more hooks to be added to main code.
"TS" == Timo Sirainen <tss@iki.fi> writes:
TS> In any case login/master processes wouldn't have to know anything
TS> about GSSAPI there, they'd just forward parameters from
TS> dovecot-auth blindly (I think master already does?).
I'm not sure it does. I've tried handing back gssapi_qop, gssapi_max_size and gssapi_context as extra fields but I've not obviously seen those fields available in the imap process.
TS> plus side there is that if there happens to be any exploitable
TS> security holes in GSSAPI library, login process would be running
TS> in chroot and attacker wouldn't get direct access to anyone's
TS> mails.
Couldn't this be a downside also? The login process would hold the user credentials but is running as the same user as all the other imap-login/proxy processes - if there were a way to get at the process address space of the other processes one could steal the credentials. I can't decide if this is just a theoretical possibility or a credible risk.
On Wed, 2005-10-19 at 15:21 +0100, pod@herald.ox.ac.uk wrote:
"TS" == Timo Sirainen <tss@iki.fi> writes:
TS> In any case login/master processes wouldn't have to know anything TS> about GSSAPI there, they'd just forward parameters from TS> dovecot-auth blindly (I think master already does?).
I'm not sure it does. I've tried handing back gssapi_qop, gssapi_max_size and gssapi_context as extra fields but I've not obviously seen those fields available in the imap process.
Hmm. If userdb returns unknown fields, they are just put to environment variables. That's why setting "quota" in userdb reply reaches the quota plugin even though nothing in Dovecot's code knows about it.
TS> plus side there is that if there happens to be any exploitable TS> security holes in GSSAPI library, login process would be running TS> in chroot and attacker wouldn't get direct access to anyone's TS> mails.
Couldn't this be a downside also? The login process would hold the user credentials but is running as the same user as all the other imap-login/proxy processes - if there were a way to get at the process address space of the other processes one could steal the credentials. I can't decide if this is just a theoretical possibility or a credible risk.
Those processes are treated as setuid-processes by kernel, so they can't touch eachothers unless there is some kernel bug.. AFAIK the only thing they could be able to do to eachothers is to send signals.
"TS" == Timo Sirainen <tss@iki.fi> writes:
TS> Those processes are treated as setuid-processes by kernel, so they
TS> can't touch eachothers unless there is some kernel bug.. AFAIK the
TS> only thing they could be able to do to eachothers is to send
TS> signals.
Is this true of all kernels or just linux kernels?
On Thu, 2005-10-20 at 12:58 +0100, pod@herald.ox.ac.uk wrote:
"TS" == Timo Sirainen <tss@iki.fi> writes:
TS> Those processes are treated as setuid-processes by kernel, so they TS> can't touch eachothers unless there is some kernel bug.. AFAIK the TS> only thing they could be able to do to eachothers is to send TS> signals.
Is this true of all kernels or just linux kernels?
All.
"JV" == Jelmer Vernooij <jelmer@samba.org> writes:
JV> Yeah, Timo mentioned there was someone else working on a new patch
JV> when I first talked to him yesterday evening, but I already had
JV> everything working by then, except for the configuration options,
JV> so I decided to go ahead. What's the status of your patch?
Similar to yours I think. Auth only working. I didn't do a keytab config var addition. I was holding off until I'd got at least a skeleton/sketch of how to put in the SASL security layer.
JV> I've focussed on authentication only for now since it keeps the
JV> patch small and readable (and thus is hopefully more easily
JV> accepted into CVS). Just authentication is sufficient for a lot of
JV> people.
Agree.
JV> You'd have to export the GSS security context from the login
JV> process to the user process somehow, but that shouldn't be a
JV> problem with gss_{ex,im}port_sec_context().
I've been trying to work out how to propagate the exported blob safely through the dovecot process hierarchy. I'm not entirely happy about it appearing in the process environment, for example, since that may not be private enough.
I believe there is a significant privacy issue with the recently posted updated GSSAPI patch when used on multiuser systems (indeed this problem is present in the previous patch as well).
The username, and consequently the uid that the imap process is eventually run as, is taken from the authorization id in the (unwrapped) buffer supplied by the client as part of the SASL security layer negotiation.
The short of it is that with the patch as it stands user1 can authenticate as user1 but supply user2 as the authorization id and dovecot will fire up an imap process running as user2 looking at user2's mail.
I'm attaching a patch that I believe fixes the immediate problem but it is really only a bandaid over more general SASL issues.
The patch does the following:
- Adds in a QOP negotiation flags enum (somewhat cosmetic really)
- Renames src_name in gssapi_auth_request to authn_name and adds authz_name
- Adds a helper function to import a gss_name_t from a buffer
- Imports the authorization id (authz_name) and compares it with the authentication id (authn_name). If they are different authentication is declined.
I'm not entirely happy that the username passed back from the auth process is still copied from the authorization id buffer because it feels like the username should come from gss_display_name. Unfortunately that will tend to return names like user@REALM which opens up another can of worms and is too much for my little brain to cope with right now.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
pod@herald.ox.ac.uk wrote:
I believe there is a significant privacy issue with the recently posted updated GSSAPI patch when used on multiuser systems (indeed this problem is present in the previous patch as well).
The username, and consequently the uid that the imap process is eventually run as, is taken from the authorization id in the (unwrapped) buffer supplied by the client as part of the SASL security layer negotiation.
Arrgh!! - stupid of me not having thought of this. It's actually in the internet draft...:
"[..] and the remaining octets as the authorization identity. [..] The server must verify that the src_name is authorized to act as the authorization identity. After these verifications, the authetnication process is complete."
Thanks for pointing this out.
I'm attaching a patch that I believe fixes the immediate problem but it is really only a bandaid over more general SASL issues.
If you'd want to use dovecot's SASL for more advanced stuff, then maybe, yes. I wouldn't consider this patch a bandaid though (that sounds hackish).
I'm not entirely happy that the username passed back from the auth process is still copied from the authorization id buffer because it feels like the username should come from gss_display_name. Unfortunately that will tend to return names like user@REALM which opens up another can of worms and is too much for my little brain to cope with right now.
No, what you did here is correct. The second name is the one that should be returned to dovecot (see the internet draft).
Cheers,
Jelmer -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2 (GNU/Linux)
iD8DBQFDV8SCPa9Uoh7vUnYRAlvxAJ9I+1teJqvx0A0iAg8T4kJtGerHIgCdG0F7 OHiTxqwS4xwuJ8pN75+r52w= =Iql/ -----END PGP SIGNATURE-----
participants (3)
-
Jelmer Vernooij
-
pod@herald.ox.ac.uk
-
Timo Sirainen