X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fauths%2Fcyrus_sasl.c;h=cbf2446338d8f34fedebf1e4c5a6e1d6773ed891;hb=d5b80e59458182b2d557a929a18cb8c70cd56b68;hp=7e6603988b3fff0d67e9740182954c57c6df205f;hpb=f78eb7c6264c5f1a4ec2fb24c39060e0686f7714;p=exim.git diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index 7e6603988..cbf244633 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/auths/cyrus_sasl.c,v 1.4 2006/02/10 14:25:43 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2003 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This code was originally contributed by Matthew Byng-Maddick */ @@ -27,7 +25,9 @@ in a dummy argument to stop even pickier compilers complaining about infinite loops. */ #ifndef AUTH_CYRUS_SASL -static void dummy(int x) { dummy(x-1); } +static void dummy(int x); +static void dummy2(int x) { dummy(x-1); } +static void dummy(int x) { dummy2(x-1); } #else @@ -53,7 +53,7 @@ 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. */ +/* Default private options block for the cyrus_sasl authentication method. */ auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = { US"smtp", /* server_service */ @@ -63,6 +63,20 @@ auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = { }; +#ifdef MACRO_PREDEF + +/* Dummy values */ +void auth_cyrus_sasl_init(auth_instance *ablock) {} +int auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) {return 0;} +int auth_cyrus_sasl_client(auth_instance *ablock, smtp_inblock *inblock, + smtp_outblock *outblock, int timeout, uschar *buffer, int buffsize) {return 0;} +void auth_cyrus_sasl_version_report(FILE *f) {} + +#else /*!MACRO_PREDEF*/ + + + + /************************************************* * Initialization entry point * *************************************************/ @@ -97,10 +111,11 @@ auth_cyrus_sasl_init(auth_instance *ablock) { auth_cyrus_sasl_options_block *ob = (auth_cyrus_sasl_options_block *)(ablock->options_block); -uschar *list, *listptr, *buffer; +const uschar *list, *listptr, *buffer; int rc, i; unsigned int len; -uschar *rs_point; +uschar *rs_point, *expanded_hostname; +char *realm_expanded; sasl_conn_t *conn; sasl_callback_t cbs[]={ @@ -111,11 +126,26 @@ sasl_callback_t cbs[]={ if(ob->server_mech == NULL) ob->server_mech=string_copy(ablock->public_name); +expanded_hostname = expand_string(ob->server_hostname); +if (expanded_hostname == NULL) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " + "couldn't expand server_hostname [%s]: %s", + ablock->name, ob->server_hostname, expand_string_message); + +realm_expanded=NULL; +if (ob->server_realm != NULL) { + realm_expanded = CS expand_string(ob->server_realm); + if (realm_expanded == NULL) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " + "couldn't expand server_realm [%s]: %s", + ablock->name, ob->server_realm, expand_string_message); +} + /* we're going to initialise the library to check that there is an * authenticator of type whatever mechanism we're using */ -cbs[0].proc = &mysasl_config; +cbs[0].proc = (int(*)(void)) &mysasl_config; cbs[0].context = ob->server_mech; rc=sasl_server_init(cbs, "exim"); @@ -124,13 +154,13 @@ 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(CS ob->server_service, CS primary_hostname, - CS ob->server_realm, NULL, NULL, NULL, 0, &conn); +rc=sasl_server_new(CS ob->server_service, CS expanded_hostname, + realm_expanded, 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, "", ":", "", (const char **)(&list), &len, &i); +rc=sasl_listmech(conn, NULL, "", ":", "", (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); @@ -138,7 +168,11 @@ if( rc != SASL_OK ) i=':'; listptr=list; -HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list); +HDEBUG(D_auth) { + debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n", + ob->server_service, expanded_hostname, realm_expanded); + debug_printf("Cyrus SASL knows mechanisms: %s\n", list); +} /* the store_get / store_reset mechanism is hierarchical * the hierarchy is stored for us behind our back. This point @@ -186,7 +220,8 @@ uschar *output, *out2, *input, *clear, *hname; uschar *debug = NULL; /* Stops compiler complaining */ sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}}; sasl_conn_t *conn; -int rc, firsttime=1, clen; +char *realm_expanded; +int rc, i, firsttime=1, clen, *negotiated_ssf_ptr=NULL, negotiated_ssf; unsigned int inlen, outlen; input=data; @@ -195,7 +230,11 @@ inlen=Ustrlen(data); HDEBUG(D_auth) debug=string_copy(data); hname=expand_string(ob->server_hostname); -if(hname == NULL) +realm_expanded=NULL; +if (hname && ob->server_realm) + realm_expanded= CS expand_string(ob->server_realm); +if((hname == NULL) || + ((realm_expanded == NULL) && (ob->server_realm != NULL))) { auth_defer_msg = expand_string_message; return DEFER; @@ -203,7 +242,7 @@ if(hname == NULL) if(inlen) { - clen=auth_b64decode(input, &clear); + clen = b64decode(input, &clear); if(clen < 0) { return BAD64; @@ -219,9 +258,13 @@ if (rc != SASL_OK) return DEFER; } -rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL, +rc=sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL, NULL, NULL, 0, &conn); +HDEBUG(D_auth) + debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n", + ob->server_service, hname, realm_expanded); + if( rc != SASL_OK ) { auth_defer_msg = US"couldn't initialise Cyrus SASL connection"; @@ -229,6 +272,81 @@ if( rc != SASL_OK ) return DEFER; } +if (tls_in.cipher) + { + rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits); + if (rc != SASL_OK) + { + HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n", + tls_in.bits, sasl_errstring(rc, NULL, NULL)); + auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF"; + sasl_done(); + return DEFER; + } + else + HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits); + } +else + HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n"); + +/* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly +annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed +with their iptostring() function, which just wraps +getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the +inet_ntop which we wrap in our host_ntoa() function. + +So the docs are too strict and we shouldn't worry about :: contractions. */ + +/* Set properties for remote and local host-ip;port */ +for (i=0; i < 2; ++i) + { + struct sockaddr_storage ss; + int (*query)(int, struct sockaddr *, socklen_t *); + int propnum, port; + const uschar *label; + uschar *address, *address_port; + const char *s_err; + socklen_t sslen; + + if (i) + { + query = &getpeername; + propnum = SASL_IPREMOTEPORT; + label = CUS"peer"; + } + else + { + query = &getsockname; + propnum = SASL_IPLOCALPORT; + label = CUS"local"; + } + + sslen = sizeof(ss); + rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen); + if (rc < 0) + { + HDEBUG(D_auth) + debug_printf("Failed to get %s address information: %s\n", + label, strerror(errno)); + break; + } + + address = host_ntoa(-1, &ss, NULL, &port); + address_port = string_sprintf("%s;%d", address, port); + + rc = sasl_setprop(conn, propnum, address_port); + if (rc != SASL_OK) + { + s_err = sasl_errdetail(conn); + HDEBUG(D_auth) + debug_printf("Failed to set %s SASL property: [%d] %s\n", + label, rc, s_err ? s_err : ""); + break; + } + HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n", + label, address_port); + } + rc=SASL_CONTINUE; while(rc==SASL_CONTINUE) @@ -259,7 +377,7 @@ while(rc==SASL_CONTINUE) HDEBUG(D_auth) debug=string_copy(input); if(inlen) { - clen=auth_b64decode(input, &clear); + clen = b64decode(input, &clear); if(clen < 0) { sasl_dispose(&conn); @@ -327,22 +445,81 @@ while(rc==SASL_CONTINUE) /* Get the username and copy it into $auth1 and $1. The former is now the preferred variable; the latter is the original variable. */ rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2)); + if (rc != SASL_OK) + { + HDEBUG(D_auth) + debug_printf("Cyrus SASL library will not tell us the username: %s\n", + sasl_errstring(rc, NULL, NULL)); + log_write(0, LOG_REJECT, "%s authenticator (%s):\n " + "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech, + sasl_errstring(rc, NULL, NULL)); + sasl_dispose(&conn); + sasl_done(); + return FAIL; + } + auth_vars[0] = 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); + debug_printf("Cyrus SASL %s authentication succeeded for %s\n", + ob->server_mech, auth_vars[0]); + + rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)); + if (rc != SASL_OK) + { + HDEBUG(D_auth) + debug_printf("Cyrus SASL library will not tell us the SSF: %s\n", + sasl_errstring(rc, NULL, NULL)); + log_write(0, LOG_REJECT, "%s authenticator (%s):\n " + "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech, + sasl_errstring(rc, NULL, NULL)); + sasl_dispose(&conn); + sasl_done(); + return FAIL; + } + negotiated_ssf = *negotiated_ssf_ptr; + HDEBUG(D_auth) + debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf); + if (negotiated_ssf > 0) + { + HDEBUG(D_auth) + debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf); + log_write(0, LOG_REJECT, "%s authenticator (%s):\n " + "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf); + sasl_dispose(&conn); + sasl_done(); + return FAIL; + } + /* close down the connection, freeing up library's memory */ sasl_dispose(&conn); sasl_done(); - return OK; + + /* Expand server_condition as an authorization check */ + return auth_check_serv_cond(ablock); } } /* NOTREACHED */ return 0; /* Stop compiler complaints */ } +/************************************************* +* Diagnostic API * +*************************************************/ + +void +auth_cyrus_sasl_version_report(FILE *f) +{ + const char *implementation, *version; + sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL); + fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n" + " Runtime: %s [%s]\n", + SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP, + version, implementation); +} + /************************************************* * Client entry point * *************************************************/ @@ -362,6 +539,7 @@ auth_cyrus_sasl_client( return FAIL; } +#endif /*!MACRO_PREDEF*/ #endif /* AUTH_CYRUS_SASL */ /* End of cyrus_sasl.c */