From: DINH V. H. <ho...@us...> - 2005-07-16 17:56:06
|
Update of /cvsroot/libetpan/libetpan/src/low-level/smtp In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4921/src/low-level/smtp Modified Files: mailsmtp.c mailsmtp.h mailsmtp_types.h Log Message: SASL is implemented for POP3, IMAP4 and SMTP Index: mailsmtp.c =================================================================== RCS file: /cvsroot/libetpan/libetpan/src/low-level/smtp/mailsmtp.c,v retrieving revision 1.19 retrieving revision 1.20 diff -u -d -r1.19 -r1.20 --- mailsmtp.c 1 Jun 2005 16:04:46 -0000 1.19 +++ mailsmtp.c 16 Jul 2005 17:55:57 -0000 1.20 @@ -35,6 +35,11 @@ * $Id$ */ +#ifndef CONFIG_H +#define CONFIG_H +#include "config.h" +#endif + #include "mailsmtp.h" #include "connect.h" #include "md5.h" @@ -51,6 +56,12 @@ #endif #include <stdio.h> +#ifdef USE_SASL +#include <sasl/sasl.h> +#include <sasl/saslutil.h> +#endif + +#include "mailsasl.h" /* RFC 2821 : SMTP @@ -102,7 +113,11 @@ session->esmtp = 0; session->auth = MAILSMTP_AUTH_NOT_CHECKED; - + +#ifdef USE_SASL + session->smtp_sasl.sasl_conn = NULL; +#endif + return session; free_line_buffer: @@ -115,6 +130,13 @@ void mailsmtp_free(mailsmtp * session) { +#ifdef USE_SASL + if (session->smtp_sasl.sasl_conn != NULL) { + sasl_dispose((sasl_conn_t **) &session->smtp_sasl.sasl_conn); + mailsasl_unref(); + } +#endif + if (session->stream) mailsmtp_quit(session); @@ -385,6 +407,9 @@ } else if (strncasecmp(response, "PLAIN", 5) == 0) { session->auth |= MAILSMTP_AUTH_PLAIN; response += 5; + } else if (strncasecmp(response, "DIGEST-MD5", 10) == 0) { + session->auth |= MAILSMTP_AUTH_DIGEST_MD5; + response += 10; } else { /* unknown auth method - jump to next word or eol */ while (!isdelim(response[0]) || response[0] == '\r') @@ -651,6 +676,7 @@ return err; } +#if 0 static int mailsmtp_auth_plain(mailsmtp * session, const char * user, const char * pass) { @@ -772,10 +798,12 @@ free(response); return err; } +#endif int mailsmtp_auth_type(mailsmtp * session, const char * user, const char * pass, int type) { +#if 0 /* deprecated */ int err; char command[SMTP_STRING_SIZE]; @@ -815,6 +843,44 @@ default: return MAILSMTP_ERROR_NOT_IMPLEMENTED; } +#endif + int err; + char command[SMTP_STRING_SIZE]; + + if (session->auth == MAILSMTP_AUTH_NOT_CHECKED) + return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND; + + if ( !(session->auth & type) ) return MAILSMTP_ERROR_AUTH_NOT_SUPPORTED; + + switch (type) { + case MAILSMTP_AUTH_LOGIN: + { + snprintf(command, SMTP_STRING_SIZE, "AUTH LOGIN\r\n"); + err = send_command(session, command); + if (err == -1) return MAILSMTP_ERROR_STREAM; + + err = read_response(session); + err = auth_map_errors(err); + if (err != MAILSMTP_NO_ERROR) return err; + + return mailsmtp_auth_login(session, user, pass); + } + case MAILSMTP_AUTH_PLAIN: + return mailesmtp_auth_sasl(session, "PLAIN", + NULL, NULL, NULL, user, user, pass, NULL); + + case MAILSMTP_AUTH_CRAM_MD5: + return mailesmtp_auth_sasl(session, "CRAM-MD5", + NULL, NULL, NULL, user, user, pass, NULL); + + case MAILSMTP_AUTH_DIGEST_MD5: + return mailesmtp_auth_sasl(session, "DIGEST-MD5", + NULL, NULL, NULL, user, user, pass, NULL); + + default: + return MAILSMTP_ERROR_NOT_IMPLEMENTED; + } + } @@ -823,7 +889,9 @@ if (session->auth == MAILSMTP_AUTH_NOT_CHECKED) return MAILSMTP_ERROR_BAD_SEQUENCE_OF_COMMAND; - if (session->auth & MAILSMTP_AUTH_CRAM_MD5) { + if (session->auth & MAILSMTP_AUTH_DIGEST_MD5) { + return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_DIGEST_MD5); + } else if (session->auth & MAILSMTP_AUTH_CRAM_MD5) { return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_CRAM_MD5); } else if (session->auth & MAILSMTP_AUTH_PLAIN) { return mailsmtp_auth_type(session, user, pass, MAILSMTP_AUTH_PLAIN); @@ -986,3 +1054,255 @@ return "Unknown error code"; } } + + +#ifdef USE_SASL +static int sasl_getsimple(void * context, int id, + const char ** result, unsigned * len) +{ + mailsmtp * session; + + session = context; + + switch (id) { + case SASL_CB_USER: + if (result != NULL) + * result = session->smtp_sasl.sasl_login; + if (len != NULL) + * len = strlen(session->smtp_sasl.sasl_login); + return SASL_OK; + + case SASL_CB_AUTHNAME: + if (result != NULL) + * result = session->smtp_sasl.sasl_auth_name; + if (len != NULL) + * len = strlen(session->smtp_sasl.sasl_auth_name); + return SASL_OK; + } + + return SASL_FAIL; +} + +static int sasl_getsecret(sasl_conn_t * conn, void * context, int id, + sasl_secret_t ** psecret) +{ + mailsmtp * session; + + session = context; + + switch (id) { + case SASL_CB_PASS: + if (psecret != NULL) + * psecret = session->smtp_sasl.sasl_secret; + return SASL_OK; + } + + return SASL_FAIL; +} + +static int sasl_getrealm(void * context, int id, + const char ** availrealms, + const char ** result) +{ + mailsmtp * session; + + session = context; + + switch (id) { + case SASL_CB_GETREALM: + if (result != NULL) + * result = session->smtp_sasl.sasl_realm; + return SASL_OK; + } + + return SASL_FAIL; +} +#endif + +int mailesmtp_auth_sasl(mailsmtp * session, const char * auth_type, + const char * server_fqdn, + const char * local_ip_port, + const char * remote_ip_port, + const char * login, const char * auth_name, + const char * password, const char * realm) +{ +#ifdef USE_SASL + int r; + char command[SMTP_STRING_SIZE]; + sasl_callback_t sasl_callback[5]; + const char * sasl_out; + unsigned sasl_out_len; + const char * mechusing; + sasl_secret_t * secret; + int res; + size_t len; + char * encoded; + unsigned int encoded_len; + unsigned int max_encoded; + + sasl_callback[0].id = SASL_CB_GETREALM; + sasl_callback[0].proc = sasl_getrealm; + sasl_callback[0].context = session; + sasl_callback[1].id = SASL_CB_USER; + sasl_callback[1].proc = sasl_getsimple; + sasl_callback[1].context = session; + sasl_callback[2].id = SASL_CB_AUTHNAME; + sasl_callback[2].proc = sasl_getsimple; + sasl_callback[2].context = session; + sasl_callback[3].id = SASL_CB_PASS; + sasl_callback[3].proc = sasl_getsecret; + sasl_callback[3].context = session; + sasl_callback[4].id = SASL_CB_LIST_END; + sasl_callback[4].proc = NULL; + sasl_callback[4].context = NULL; + + len = strlen(password); + secret = malloc(sizeof(* secret) + len); + if (secret == NULL) { + res = MAILSMTP_ERROR_MEMORY; + goto err; + } + secret->len = len; + memcpy(secret->data, password, len + 1); + + session->smtp_sasl.sasl_server_fqdn = server_fqdn; + session->smtp_sasl.sasl_login = login; + session->smtp_sasl.sasl_auth_name = auth_name; + session->smtp_sasl.sasl_password = password; + session->smtp_sasl.sasl_realm = realm; + session->smtp_sasl.sasl_secret = secret; + + /* init SASL */ + mailsasl_ref(); + + r = sasl_client_new("smtp", server_fqdn, + local_ip_port, remote_ip_port, sasl_callback, 0, + (sasl_conn_t **) &session->smtp_sasl.sasl_conn); + if (r != SASL_OK) { + res = MAILSMTP_ERROR_AUTH_LOGIN; + goto free_secret; + } + + r = sasl_client_start(session->smtp_sasl.sasl_conn, + auth_type, NULL, &sasl_out, &sasl_out_len, &mechusing); + if ((r != SASL_CONTINUE) && (r != SASL_OK)) { + res = MAILSMTP_ERROR_AUTH_LOGIN; + goto free_secret; + } + + if (sasl_out_len != 0) { + max_encoded = ((sasl_out_len + 2) / 3) * 4; + encoded = malloc(max_encoded + 1); + if (encoded == NULL) { + res = MAILSMTP_ERROR_MEMORY; + goto free_secret; + } + + r = sasl_encode64(sasl_out, sasl_out_len, + encoded, max_encoded + 1, &encoded_len); + if (r != SASL_OK) { + free(encoded); + res = MAILSMTP_ERROR_MEMORY; + goto free_secret; + } + + snprintf(command, SMTP_STRING_SIZE, "AUTH %s %s\r\n", auth_type, encoded); + + free(encoded); + } + else { + snprintf(command, SMTP_STRING_SIZE, "AUTH %s\r\n", auth_type); + } + + r = send_command(session, command); + if (r == -1) { + res = MAILSMTP_ERROR_STREAM; + goto free_secret; + } + + while (1) { + r = read_response(session); + switch (r) { + case 220: + case 235: + free(session->smtp_sasl.sasl_secret); + return MAILSMTP_NO_ERROR; + + case 334: + { + size_t response_len; + char * decoded; + unsigned int decoded_len; + unsigned int max_decoded; + + response_len = strlen(session->response); + max_decoded = response_len * 3 / 4; + decoded = malloc(max_decoded + 1); + if (decoded == NULL) { + res = MAILSMTP_ERROR_MEMORY; + goto free_secret; + } + + r = sasl_decode64(session->response, response_len, + decoded, max_decoded + 1, &decoded_len); + + if (r != SASL_OK) { + free(decoded); + res = MAILSMTP_ERROR_MEMORY; + goto free_secret; + } + + r = sasl_client_step(session->smtp_sasl.sasl_conn, + decoded, decoded_len, NULL, &sasl_out, &sasl_out_len); + + free(decoded); + + if ((r != SASL_CONTINUE) && (r != SASL_OK)) { + res = MAILSMTP_ERROR_AUTH_LOGIN; + goto free_secret; + } + + max_encoded = ((sasl_out_len + 2) / 3) * 4; + encoded = malloc(max_encoded + 1); + if (encoded == NULL) { + res = MAILSMTP_ERROR_MEMORY; + goto free_secret; + } + + r = sasl_encode64(sasl_out, sasl_out_len, + encoded, max_encoded + 1, &encoded_len); + if (r != SASL_OK) { + free(encoded); + res = MAILSMTP_ERROR_MEMORY; + goto free_secret; + } + + snprintf(command, SMTP_STRING_SIZE, "%s\r\n", encoded); + r = send_command(session, command); + + free(encoded); + + if (r == -1) { + res = MAILSMTP_ERROR_STREAM; + goto free_secret; + } + } + break; + + default: + res = auth_map_errors(r); + goto free_secret; + } + } + + return MAILSMTP_NO_ERROR; + + free_secret: + free(session->smtp_sasl.sasl_secret); + err: + return res; +#else + return MAILSMTP_ERROR_NOT_IMPLEMENTED; +#endif +} + Index: mailsmtp.h =================================================================== RCS file: /cvsroot/libetpan/libetpan/src/low-level/smtp/mailsmtp.h,v retrieving revision 1.16 retrieving revision 1.17 diff -u -d -r1.16 -r1.17 --- mailsmtp.h 21 Nov 2004 21:53:40 -0000 1.16 +++ mailsmtp.h 16 Jul 2005 17:55:57 -0000 1.17 @@ -54,12 +54,15 @@ int mailsmtp_connect(mailsmtp * session, mailstream * s); int mailsmtp_quit(mailsmtp * session); + +/* This call is deprecated and mailesmtp_auth_sasl() should be used instead */ /** * Tries AUTH with detected method - "better" method first: * CRAM-MD5 -> PLAIN -> LOGIN */ int mailsmtp_auth(mailsmtp * session, const char * user, const char * pass); +/* This call is deprecated and mailesmtp_auth_sasl() should be used instead */ /** * tries to autenticate with the server using given auth-type * returns MAILSMTP_NO_ERROR on success @@ -87,6 +90,13 @@ const char * mailsmtp_strerror(int errnum); +int mailesmtp_auth_sasl(mailsmtp * session, const char * auth_type, + const char * server_fqdn, + const char * local_ip_port, + const char * remote_ip_port, + const char * login, const char * auth_name, + const char * password, const char * realm); + #ifdef __cplusplus } #endif Index: mailsmtp_types.h =================================================================== RCS file: /cvsroot/libetpan/libetpan/src/low-level/smtp/mailsmtp_types.h,v retrieving revision 1.12 retrieving revision 1.13 diff -u -d -r1.12 -r1.13 --- mailsmtp_types.h 1 Jun 2005 20:23:23 -0000 1.12 +++ mailsmtp_types.h 16 Jul 2005 17:55:57 -0000 1.13 @@ -78,7 +78,8 @@ MAILSMTP_AUTH_CHECKED = 1, MAILSMTP_AUTH_CRAM_MD5 = 2, MAILSMTP_AUTH_PLAIN = 4, - MAILSMTP_AUTH_LOGIN = 8 + MAILSMTP_AUTH_LOGIN = 8, + MAILSMTP_AUTH_DIGEST_MD5 = 16, }; enum { @@ -104,6 +105,16 @@ int esmtp; /* contains flags MAILSMTP_ESMTP_* */ int auth; /* contains flags MAILSMTP_AUTH_* */ + + struct { + void * sasl_conn; + const char * sasl_server_fqdn; + const char * sasl_login; + const char * sasl_auth_name; + const char * sasl_password; + const char * sasl_realm; + void * sasl_secret; + } smtp_sasl; }; typedef struct mailsmtp mailsmtp; |