[dovecot-cvs] dovecot/src/auth Makefile.am, 1.48, 1.49 auth.c, 1.19, 1.20 mech-anonymous.c, 1.13, 1.14 mech-apop.c, 1.16, 1.17 mech-cram-md5.c, 1.20, 1.21 mech-digest-md5.c, 1.34, 1.35 mech-gssapi.c, NONE, 1.1 mech-login.c, 1.12, 1.13 mech-ntlm.c, 1.19, 1.20 mech-plain.c, 1.31, 1.32 mech-rpa.c, 1.19, 1.20 mech.c, 1.54, 1.55 mech.h, 1.35, 1.36

cras at dovecot.org cras at dovecot.org
Thu Oct 27 17:57:57 EEST 2005


Update of /var/lib/cvs/dovecot/src/auth
In directory talvi:/tmp/cvs-serv20814/src/auth

Modified Files:
	Makefile.am auth.c mech-anonymous.c mech-apop.c 
	mech-cram-md5.c mech-digest-md5.c mech-login.c mech-ntlm.c 
	mech-plain.c mech-rpa.c mech.c mech.h 
Added Files:
	mech-gssapi.c 
Log Message:
Added GSSAPI support. Patch by Jelmer Vernooij and some fixes by
pod at herald.ox.ac.uk



Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/Makefile.am,v
retrieving revision 1.48
retrieving revision 1.49
diff -u -d -r1.48 -r1.49
--- Makefile.am	19 Oct 2005 13:38:36 -0000	1.48
+++ Makefile.am	27 Oct 2005 14:57:50 -0000	1.49
@@ -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: auth.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- auth.c	16 Oct 2005 14:06:59 -0000	1.19
+++ auth.c	27 Oct 2005 14:57:50 -0000	1.20
@@ -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,8 @@
 	struct mech_module_list *list;
 
 	for (list = auth->mech_modules; list != NULL; list = list->next) {
+		if (list->module.need_passdb && auth->passdbs == NULL)
+			break;
 		if (list->module.passdb_need_plain &&
 		    !auth_passdb_list_have_plain(auth))
 			break;

Index: mech-anonymous.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-anonymous.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- mech-anonymous.c	23 Apr 2005 09:11:49 -0000	1.13
+++ mech-anonymous.c	27 Oct 2005 14:57:50 -0000	1.14
@@ -57,6 +57,7 @@
 
 	MEMBER(flags) MECH_SEC_ANONYMOUS,
 
+	MEMBER(need_passdb) TRUE,
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) FALSE,
 

Index: mech-apop.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-apop.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- mech-apop.c	23 Apr 2005 09:11:49 -0000	1.16
+++ mech-apop.c	27 Oct 2005 14:57:50 -0000	1.17
@@ -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: mech-cram-md5.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-cram-md5.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- mech-cram-md5.c	8 Jan 2005 21:37:32 -0000	1.20
+++ mech-cram-md5.c	27 Oct 2005 14:57:50 -0000	1.21
@@ -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: mech-digest-md5.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-digest-md5.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- mech-digest-md5.c	8 Jan 2005 21:37:32 -0000	1.34
+++ mech-digest-md5.c	27 Oct 2005 14:57:51 -0000	1.35
@@ -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,
 

--- NEW FILE: mech-gssapi.c ---
/*
 * GSSAPI Module
 *
 * Copyright (c) 2005 Jelmer Vernooij <jelmer at 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 "str-sanitize.h"
#include "buffer.h"
#include "hex-binary.h"
#include "safe-memset.h"
#include "hostpid.h"

#ifdef HAVE_GSSAPI

#include <gssapi/gssapi.h>

/* Non-zero flags defined in RFC 2222 */
enum sasl_gssapi_qop {
	SASL_GSSAPI_QOP_UNSPECIFIED = 0x00,
	SASL_GSSAPI_QOP_AUTH_ONLY   = 0x01,
	SASL_GSSAPI_QOP_AUTH_INT    = 0x02,
	SASL_GSSAPI_QOP_AUTH_CONF   = 0x04
};

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 authn_name;
	gss_name_t authz_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,
			str_sanitize(status_string.value, (size_t)-1));

		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(128);
	str_append(principal_name, t_str_lcase(request->service));
	str_append_c(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 = str_c_modifyable(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 gss_name_t
import_name(struct auth_request *request, void *str, size_t len)
{
	OM_uint32 major_status, minor_status;
	gss_buffer_desc name_buf;
	gss_name_t name;

	name_buf.value = str;
	name_buf.length = len;
	major_status = gss_import_name(&minor_status,
				       &name_buf,
				       GSS_C_NO_OID,
				       &name);
	if (GSS_ERROR(major_status)) {
		auth_request_log_gss_error(request, major_status,
					   GSS_C_GSS_CODE, "gss_import_name");
		return GSS_C_NO_NAME;
	}

	return name;
}

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->authn_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;
	unsigned char ret[4];

	/* The clients return data should be empty here */
	
	/* Only authentication, no integrity or confidentiality
	   protection (yet?) */
	ret[0] = (SASL_GSSAPI_QOP_UNSPECIFIED |
                  SASL_GSSAPI_QOP_AUTH_ONLY);
	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;
	int equal_authn_authz = 0;

	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->authz_name = import_name(&request->auth_request,
					  (unsigned char *)outbuf.value + 4,
					  outbuf.length - 4);
	if ((request->authn_name == GSS_C_NO_NAME) ||
	    (request->authz_name == GSS_C_NO_NAME)) {
		/* XXX (pod): is this check necessary? */
		auth_request_log_error(&request->auth_request, "gssapi",
			"one of authn_name or authz_name not determined");
		auth_request_fail(&request->auth_request);
		return;
	}
	major_status = gss_compare_name(&minor_status,
					request->authn_name,
					request->authz_name,
					&equal_authn_authz);
	if (equal_authn_authz == 0) {
		auth_request_log_error(&request->auth_request, "gssapi",
			"authn_name and authz_name differ: not supported");
		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 = (void *)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->authn_name = GSS_C_NO_NAME;
	gssapi_request->authz_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);
	major_status = gss_release_name(&minor_status,
					&gssapi_request->authn_name);
	major_status = gss_release_name(&minor_status,
					&gssapi_request->authz_name);

	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: mech-login.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-login.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- mech-login.c	23 Apr 2005 09:11:49 -0000	1.12
+++ mech-login.c	27 Oct 2005 14:57:51 -0000	1.13
@@ -87,6 +87,7 @@
 
 	MEMBER(flags) MECH_SEC_PLAINTEXT,
 
+	MEMBER(need_passdb) TRUE,
 	MEMBER(passdb_need_plain) TRUE,
 	MEMBER(passdb_need_credentials) FALSE,
 

Index: mech-ntlm.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-ntlm.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- mech-ntlm.c	23 Apr 2005 09:11:49 -0000	1.19
+++ mech-ntlm.c	27 Oct 2005 14:57:51 -0000	1.20
@@ -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: mech-plain.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-plain.c,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -d -r1.31 -r1.32
--- mech-plain.c	23 Apr 2005 09:11:49 -0000	1.31
+++ mech-plain.c	27 Oct 2005 14:57:51 -0000	1.32
@@ -103,6 +103,7 @@
 
 	MEMBER(flags) MECH_SEC_PLAINTEXT,
 
+	MEMBER(need_passdb) TRUE,
 	MEMBER(passdb_need_plain) TRUE,
 	MEMBER(passdb_need_credentials) FALSE,
 

Index: mech-rpa.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-rpa.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- mech-rpa.c	23 Apr 2005 09:11:49 -0000	1.19
+++ mech-rpa.c	27 Oct 2005 14:57:51 -0000	1.20
@@ -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: mech.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech.c,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -d -r1.54 -r1.55
--- mech.c	7 Jan 2005 19:55:50 -0000	1.54
+++ mech.c	27 Oct 2005 14:57:51 -0000	1.55
@@ -54,6 +54,9 @@
 extern struct mech_module mech_ntlm;
 extern struct mech_module mech_rpa;
 extern struct mech_module mech_anonymous;
+#ifdef HAVE_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 HAVE_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 HAVE_GSSAPI
+	mech_unregister_module(&mech_gssapi);
+#endif
 }

Index: mech.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech.h,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- mech.h	9 Jan 2005 16:54:48 -0000	1.35
+++ mech.h	27 Oct 2005 14:57:51 -0000	1.36
@@ -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;
 



More information about the dovecot-cvs mailing list