diff -uNr OS/Makefile-Base.orig OS/Makefile-Base --- OS/Makefile-Base.orig 2003-12-01 10:15:41.000000000 +0000 +++ OS/Makefile-Base 2004-02-23 12:16:26.000000000 +0000 @@ -270,7 +270,8 @@ routers/routers.a transports/transports.a lookups/lookups.a \ auths/auths.a \ $(LIBRESOLV) $(LIBS) $(LIBS_EXIM) $(IPV6_LIBS) $(EXTRALIBS) \ - $(EXTRALIBS_EXIM) $(DBMLIB) $(LOOKUP_LIBS) $(PERL_LIBS) $(TLS_LIBS) + $(EXTRALIBS_EXIM) $(DBMLIB) $(LOOKUP_LIBS) $(AUTH_LIBS) \ + $(PERL_LIBS) $(TLS_LIBS) @if [ x"$(STRIP_COMMAND)" != x"" ]; then \ echo $(STRIP_COMMAND) exim; \ $(STRIP_COMMAND) exim; \ diff -uNr doc/OptionLists.txt.orig doc/OptionLists.txt --- doc/OptionLists.txt.orig 2003-12-01 10:15:41.000000000 +0000 +++ doc/OptionLists.txt 2004-02-23 15:36:10.000000000 +0000 @@ -415,9 +415,13 @@ serialize_hosts host list unset smtp 1.60 server_advertise_condition string* unset authenticators 4.14 server_condition string* unset plaintext 3.10 +server_hostname string* "$primary_hostname" cyrus_sasl server_mail_auth_condition string* unset authenticators 3.22 +server_mech string public_name cyrus_sasl server_prompts string* unset plaintext 3.10 +server_realm string unset cyrus_sasl server_secret string* unset cram_md5 3.10 +server_service string "smtp" cyrus_sasl server_set_id string* unset authenticators 3.10 shadow_condition string* unset transports shadow_transport string unset transports diff -uNr doc/spec.txt.orig doc/spec.txt --- doc/spec.txt.orig 2003-12-01 10:15:41.000000000 +0000 +++ doc/spec.txt 2004-02-23 12:16:26.000000000 +0000 @@ -522,6 +522,10 @@ 50. Adding new drivers or lookup types +51. The cyrus_sasl authenticator + + 51.1 Using cyrus_sasl as a server + 1. INTRODUCTION @@ -24196,3 +24200,63 @@ There is a README file in each of the sub-directories of src describing the interface that is expected. + + + + 51. THE CYRUS_SASL AUTHENTICATOR + + +The cyrus_sasl authenticator provides server support for the Cyrus library +Implementation of the RFC 2222 "Simple Authentication and Security Layer". +It provides a gatewaying mechanism directly to the cyrus interface, so if +cyrus can do, for example, CRAM-MD5, then so can the cyrus_sasl +authenticator. The code for this has been provided by Matthew Byng-Maddick +of A L Digital Ltd (http://www.aldigital.co.uk). It by default uses the +public_name of the driver to determine what mechanism to support. + +51.1 Using cyrus_sasl as a server + +The cyrus_sasl authenticator has four options, it puts the username (on a +successful authentication) into $1: + +server_hostname Type: string* Default: $primary_hostname + + This option selects the hostname we should use when communicating with the + library. + It is up to the underlying SASL plug in what it does with this data. + +server_mech Type: string Default: public_name + + This option selects the authentication mechanism this driver should use. + Examples are: + + sasl: + driver = cyrus_sasl + public_name = X-CRAM-MD5 + server_mech = CRAM-MD5 + server_set_id = $1 + + It allows you to use a different underlying mechanism from the advertised name. + +server_realm Type: string Default: unset + + This is the SASL realm that the server is claiming to be in. + +server_service Type: string Default: "smtp" + + This is the SASL service that the server claims to implement. + +In general, the cyrus_sasl authenticator is designed to work just out of the +box, using whatever mechanism the public_name of the authenticator gives it. +Thus, for a SASL library that supports CRAM-MD5 and PLAIN, then you might have +two authenticators which are: + + sasl_plain: + driver = cyrus_sasl + public_name = PLAIN + server_set_id = $1 + + sasl_cram_md5: + driver = cyrus_sasl + public_name = CRAM-MD5 + server_set_id = $1 diff -uNr scripts/MakeLinks.orig scripts/MakeLinks --- scripts/MakeLinks.orig 2003-12-01 10:15:41.000000000 +0000 +++ scripts/MakeLinks 2004-02-23 12:16:26.000000000 +0000 @@ -164,6 +164,8 @@ ln -s ../../src/auths/sha1.c sha1.c ln -s ../../src/auths/spa.c spa.c ln -s ../../src/auths/spa.h spa.h +ln -s ../../src/auths/cyrus_sasl.c cyrus_sasl.c +ln -s ../../src/auths/cyrus_sasl.h cyrus_sasl.h cd .. # The basic source files for Exim and utilities. NB local_scan.h gets linked, diff -uNr src/EDITME.orig src/EDITME --- src/EDITME.orig 2003-12-01 10:15:41.000000000 +0000 +++ src/EDITME 2004-02-23 12:16:26.000000000 +0000 @@ -389,9 +389,15 @@ # AUTH_CRAM_MD5=yes # AUTH_PLAINTEXT=yes # AUTH_SPA=yes +# AUTH_CYRUS_SASL=yes #------------------------------------------------------------------------------ +# If you specified AUTH_CYRUS_SASL above, you probably want to add the +# following: +# AUTH_LIBS=-lsasl2 + +#------------------------------------------------------------------------------ # When Exim is decoding MIME "words" in header lines, most commonly for use # in the $header_xxx expansion, it converts any foreign character sets to the # one that is set in the headers_charset option. The default setting is diff -uNr src/auths/Makefile.orig src/auths/Makefile --- src/auths/Makefile.orig 2003-12-01 10:15:41.000000000 +0000 +++ src/auths/Makefile 2004-02-23 12:16:26.000000000 +0000 @@ -7,7 +7,7 @@ OBJ = b64encode.o b64decode.o call_pam.o call_pwcheck.o call_radius.o \ xtextencode.o xtextdecode.o get_data.o get_no64_data.o md5.o \ - cram_md5.o plaintext.o pwcheck.o sha1.o auth-spa.o spa.o + cram_md5.o plaintext.o pwcheck.o sha1.o auth-spa.o spa.o cyrus_sasl.o auths.a: $(OBJ) /bin/rm -f auths.a @@ -34,6 +34,7 @@ cram_md5.o: $(HDRS) cram_md5.c cram_md5.h plaintext.o: $(HDRS) plaintext.c plaintext.h +cyrus_sasl.o: $(HDRS) cyrus_sasl.c cyrus_sasl.h spa.o: $(HDRS) spa.c spa.h # End diff -uNr src/auths/cyrus_sasl.c.orig src/auths/cyrus_sasl.c --- src/auths/cyrus_sasl.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ src/auths/cyrus_sasl.c 2004-02-23 15:53:05.000000000 +0000 @@ -0,0 +1,310 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2003 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Copyright (c) A L Digital 2004 */ + +/* A generic (mechanism independent) Cyrus SASL authenticator. */ +#include +#include "../exim.h" +#include "cyrus_sasl.h" + +/* Options specific to the cyrus_sasl authentication mechanism. */ + +optionlist auth_cyrus_sasl_options[] = { + { "server_hostname", opt_stringptr, + (void *)(offsetof(auth_cyrus_sasl_options_block, server_hostname)) }, + { "server_mech", opt_stringptr, + (void *)(offsetof(auth_cyrus_sasl_options_block, server_mech)) }, + { "server_realm", opt_stringptr, + (void *)(offsetof(auth_cyrus_sasl_options_block, server_realm)) }, + { "server_service", opt_stringptr, + (void *)(offsetof(auth_cyrus_sasl_options_block, server_service)) } +}; + +/* Size of the options list. An extern variable has to be used so that its +address can appear in the tables drtables.c. */ + +int auth_cyrus_sasl_options_count = + sizeof(auth_cyrus_sasl_options)/sizeof(optionlist); + +/* Default private options block for the contidion authentication method. */ + +auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = { + US"smtp", /* server_service */ + US"$primary_hostname", /* server_hostname */ + NULL, /* server_realm */ + NULL /* server_mech */ +}; + + +/************************************************* +* Initialization entry point * +*************************************************/ + +/* Called for each instance, after its options have been read, to +enable consistency checks to be done, or anything else that needs +to be set up. */ + +void +auth_cyrus_sasl_init(auth_instance *ablock) +{ +auth_cyrus_sasl_options_block *ob = + (auth_cyrus_sasl_options_block *)(ablock->options_block); +sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}}; +sasl_conn_t *conn; +uschar *list, *listptr, *buffer; +int rc, i; +unsigned int len; +uschar *rs_point; + +/* default the mechanism to our "public name" */ +if(ob->server_mech == NULL) + ob->server_mech=string_copy(ablock->public_name); + +/* we're going to initialise the library to check that there is an + * authenticator of type whatever mechanism we're using + */ +rc=sasl_server_init(cbs, "exim"); +if( rc != SASL_OK ) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " + "couldn't initialise Cyrus SASL library.", ablock->name); + +rc=sasl_server_new(ob->server_service, primary_hostname, + ob->server_realm, NULL, NULL, NULL, 0, &conn); +if( rc != SASL_OK ) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " + "couldn't initialise Cyrus SASL server connection.", ablock->name); + +rc=sasl_listmech(conn, NULL, US"", US":", US"", (const char **)(&list), &len, &i); +if( rc != SASL_OK ) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " + "couldn't get Cyrus SASL mechanism list.", ablock->name); + +i=':'; +listptr=list; + +HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list); + +/* the store_get / store_reset mechanism is hierarchical + * the hierarchy is stored for us behind our back. This point + * creates a hierarchy point for this function. + */ +rs_point=store_get(0); + +/* loop until either we get to the end of the list, or we match the + * public name of this authenticator + */ +while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) && + strcmpic(buffer,ob->server_mech) ); + +if(!buffer) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " + "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech); + +store_reset(rs_point); + +HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name); + +/* make sure that if we get here then we're allowed to advertise. */ +ablock->server = TRUE; + +sasl_dispose(&conn); +sasl_done(); +} + +/************************************************* +* Server entry point * +*************************************************/ + +/* For interface, see auths/README */ + +/* note, we don't care too much about memory allocation in this, because this is entirely + * within a shortlived child + */ + +int +auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) +{ +auth_cyrus_sasl_options_block *ob = + (auth_cyrus_sasl_options_block *)(ablock->options_block); +uschar *output, *out2, *input, *clear, *debug, *hname; +sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}}; +sasl_conn_t *conn; +int rc, firsttime=1, clen; +unsigned int inlen, outlen; + +input=data; +inlen=Ustrlen(data); + +HDEBUG(D_auth) debug=string_copy(data); + +hname=expand_string(ob->server_hostname); +if(hname == NULL) + { + auth_defer_msg = expand_string_message; + return DEFER; + } + +if(inlen) + { + clen=auth_b64decode(input, &clear); + if(clen < 0) + { + return BAD64; + } + input=clear; + inlen=clen; + } + +rc=sasl_server_init(cbs, "exim"); +if (rc != SASL_OK) + { + auth_defer_msg = "couldn't initialise Cyrus SASL library"; + return DEFER; + } + +rc=sasl_server_new(ob->server_service, ob->server_hostname, + ob->server_realm, NULL, NULL, NULL, 0, &conn); +if( rc != SASL_OK ) + { + auth_defer_msg = "couldn't initialise Cyrus SASL connection"; + sasl_done(); + return DEFER; + } + +rc=SASL_CONTINUE; + +while(rc==SASL_CONTINUE) + { + if(firsttime) + { + firsttime=0; + HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug); + rc=sasl_server_start(conn, ob->server_mech, inlen?input:NULL, inlen, + (const char **)(&output), &outlen); + } + else + { + /* make sure that we have a null-terminated string */ + out2=store_get(outlen+1); + memcpy(out2,output,outlen); + out2[outlen]='\0'; + if((rc=auth_get_data(&input, out2))!=OK) + { + /* we couldn't get the data, so free up the library before + * returning whatever error we get */ + sasl_dispose(&conn); + sasl_done(); + return rc; + } + inlen=Ustrlen(input); + + HDEBUG(D_auth) debug=string_copy(input); + if(inlen) + { + clen=auth_b64decode(input, &clear); + if(clen < 0) + { + sasl_dispose(&conn); + sasl_done(); + return BAD64; + } + input=clear; + inlen=clen; + } + + HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug); + rc=sasl_server_step(conn, input, inlen, (const char **)(&output), &outlen); + } + if(rc==SASL_BADPROT) + { + sasl_dispose(&conn); + sasl_done(); + return UNEXPECTED; + } + else if( rc==SASL_FAIL || rc==SASL_BUFOVER + || rc==SASL_BADMAC || rc==SASL_BADAUTH + || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT + || rc==SASL_EXPIRED || rc==SASL_DISABLED + || rc==SASL_NOUSER ) + { + /* these are considered permanent failure codes */ + HDEBUG(D_auth) + debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL)); + log_write(0, LOG_REJECT, "%s authenticator (%s):\n " + "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech, + sasl_errstring(rc, NULL, NULL)); + sasl_dispose(&conn); + sasl_done(); + return FAIL; + } + else if(rc==SASL_NOMECH) + { + /* this is a temporary failure, because the mechanism is not + * available for this user. If it wasn't available at all, we + * shouldn't have got here in the first place... + */ + HDEBUG(D_auth) + debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL)); + auth_defer_msg = + string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech); + sasl_dispose(&conn); + sasl_done(); + return DEFER; + } + else if(!(rc==SASL_OK || rc==SASL_CONTINUE)) + { + /* Anything else is a temporary failure, and we'll let SASL print out + * the error string for us + */ + HDEBUG(D_auth) + debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL)); + auth_defer_msg = + string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL)); + sasl_dispose(&conn); + sasl_done(); + return DEFER; + } + else if(rc==SASL_OK) + { + /* get the username and copy it into $1 */ + rc=sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2)); + expand_nstring[1]=string_copy(out2); + expand_nlength[1]=Ustrlen(expand_nstring[1]); + expand_nmax=1; + + HDEBUG(D_auth) + debug_printf("Cyrus SASL %s authentiction succeeded for %s\n", ob->server_mech, out2); + /* close down the connection, freeing up library's memory */ + sasl_dispose(&conn); + sasl_done(); + return OK; + } + } +/* NOTREACHED */ +} + +/************************************************* +* Client entry point * +*************************************************/ + +/* For interface, see auths/README */ + +int +auth_cyrus_sasl_client( + auth_instance *ablock, /* authenticator block */ + smtp_inblock *inblock, /* input connection */ + smtp_outblock *outblock, /* output connection */ + int timeout, /* command timeout */ + uschar *buffer, /* for reading response */ + int buffsize) /* size of buffer */ +{ +/* We don't support clients (yet) in this implementation of cyrus_sasl */ +return FAIL; +} + +/* End of cyrus_sasl.c */ diff -uNr src/auths/cyrus_sasl.h.orig src/auths/cyrus_sasl.h --- src/auths/cyrus_sasl.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ src/auths/cyrus_sasl.h 2004-02-23 12:16:26.000000000 +0000 @@ -0,0 +1,35 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2003 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Copyright (c) A L Digital Ltd 2004 */ + +/* Private structure for the private options. */ + +typedef struct { + uschar *server_service; + uschar *server_hostname; + uschar *server_realm; + uschar *server_mech; +} auth_cyrus_sasl_options_block; + +/* Data for reading the private options. */ + +extern optionlist auth_cyrus_sasl_options[]; +extern int auth_cyrus_sasl_options_count; + +/* Block containing default values. */ + +extern auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults; + +/* The entry points for the mechanism */ + +extern void auth_cyrus_sasl_init(auth_instance *); +extern int auth_cyrus_sasl_server(auth_instance *, uschar *); +extern int auth_cyrus_sasl_client(auth_instance *, smtp_inblock *, + smtp_outblock *, int, uschar *, int); + +/* End of cyrus_sasl.h */ diff -uNr src/config.h.defaults.orig src/config.h.defaults --- src/config.h.defaults.orig 2003-12-01 10:15:41.000000000 +0000 +++ src/config.h.defaults 2004-02-23 12:16:26.000000000 +0000 @@ -19,6 +19,7 @@ #define AUTH_CRAM_MD5 #define AUTH_PLAINTEXT #define AUTH_SPA +#define AUTH_CYRUS_SASL #define BIN_DIRECTORY diff -uNr src/drtables.c.orig src/drtables.c --- src/drtables.c.orig 2003-12-01 10:15:41.000000000 +0000 +++ src/drtables.c 2004-02-23 12:16:26.000000000 +0000 @@ -493,6 +493,10 @@ #include "auths/spa.h" #endif +#ifdef AUTH_CYRUS_SASL +#include "auths/cyrus_sasl.h" +#endif + auth_info auths_available[] = { /* Checking by an expansion condition on plain text */ @@ -536,6 +540,19 @@ }, #endif +#ifdef AUTH_CYRUS_SASL + { + US"cyrus_sasl", /* lookup name */ + auth_cyrus_sasl_options, + &auth_cyrus_sasl_options_count, + &auth_cyrus_sasl_option_defaults, + sizeof(auth_cyrus_sasl_options_block), + auth_cyrus_sasl_init, /* init function */ + auth_cyrus_sasl_server, /* server function */ + NULL /* client function */ + }, +#endif + { US"", NULL, NULL, NULL, 0, NULL, NULL, NULL } }; diff -uNr src/exim.c.orig src/exim.c --- src/exim.c.orig 2003-12-01 10:15:41.000000000 +0000 +++ src/exim.c 2004-02-23 12:16:26.000000000 +0000 @@ -841,6 +841,9 @@ #ifdef AUTH_SPA fprintf(f, " spa"); #endif +#ifdef AUTH_CYRUS_SASL + fprintf(f, " cyrus_sasl"); +#endif fprintf(f, "\n"); fprintf(f, "Routers:");