diff -urN elm2.4.ME+.124~/Configure elm2.4.ME+.124/Configure --- elm2.4.ME+.124~/Configure 2007-09-03 12:33:13.000000000 -0400 +++ elm2.4.ME+.124/Configure 2008-12-23 22:53:34.055876600 -0500 @@ -407,6 +407,10 @@ termlib='' i_curses='' i_term='' +gssapi='' +have_gssapi_h='' +have_gssapi_gssapi_h='' +have_gssapi_gssapi_generic_h='' d_index='' d_internet='' d_ispell='' @@ -6540,6 +6544,89 @@ i_curses="$undef" fi +have_gssapi_h="$undef" +have_gssapi_gssapi_h="$undef" +have_gssapi_gssapi_generic_h="$undef" +case "$gssapi" in +"$define") + dflt="y" + ;; +"$undef") + dflt="n" + ;; +*) + gssloc=`loc gssapi.h "" ${PKG_krb5}/include /usr/include /usr/local/include $includepath` + if $test -z "$gssloc"; then + gssloc=`loc gssapi/gssapi.h "" ${PKG_krb5}/include /usr/include /usr/local/include $includepath` + fi + if $test -z "$gssloc"; then + dflt="n" + else + dflt="y" + fi + ;; +esac +cat < +# endif +# ifdef HAVE_GSSAPI_GSSAPI_H +# include +# endif +# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H +# include +# endif +# ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE +# define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +# endif +# define GSSAUTH_P_NONE 1 + +struct gssctx { + gss_ctx_id_t context; + gss_name_t target_name; + char* username; + enum gss_state { state_begin, state_continue, state_end } state; +}; + +static const char base64digits[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define BAD -1 +static const char base64val[] = { + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, + BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,BAD, BAD,BAD,BAD,BAD, + BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD +}; +#define DECODE64(c) (isascii(c) ? base64val[c] : BAD) + +static void tob64(unsigned char* out, gss_buffer_desc* inbuf) { + int len; + unsigned char* in = inbuf->value; + + for (len = inbuf->length; len >= 3; len -= 3) { + *out++ = base64digits[in[0] >> 2]; + *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = base64digits[in[2] & 0x3f]; + in += 3; + } + if (len > 0) { + unsigned char fragment; + *out++ = base64digits[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (len > 1) + fragment |= in[1] >> 4; + *out++ = base64digits[fragment]; + *out++ = (len < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; + *out++ = '='; + } + *out++ = '\r'; + *out++ = '\n'; + *out = '\0'; +} + +static void fromb64(gss_buffer_desc* outbuf, unsigned char* in) { + unsigned char digit1, digit2, digit3, digit4; + unsigned char *out = outbuf->value; + int len = 0; + + if (in[0] == '+' && in[1] == ' ') + in += 2; + if (*in == '\r') goto bad; + do { + digit1 = in[0]; + if (DECODE64(digit1) == BAD) goto bad; + digit2 = in[1]; + if (DECODE64(digit2) == BAD) goto bad; + digit3 = in[2]; + if (digit3 != '=' && DECODE64(digit3) == BAD) goto bad; + digit4 = in[3]; + if (digit4 != '=' && DECODE64(digit4) == BAD) goto bad; + in += 4; + ++len; + *out++ = (DECODE64(digit1) << 2) | (DECODE64(digit2) >> 4); + if (digit3 != '=') { + *out++ = ((DECODE64(digit2) << 4) & 0xf0) | (DECODE64(digit3) >> 2); + ++len; + if (digit4 != '=') { + *out++ = ((DECODE64(digit3) << 6) & 0xc0) | DECODE64(digit4); + ++len; + } + } + } while (*in && *in != '\r' && digit4 != '='); + outbuf->length = len; + return; +bad: + outbuf->length = 0; + return; +} + +struct gssctx *ctx = NULL; + +int gssapi_setup(char* prot, char* hostname, char* username) { + OM_uint32 maj_stat, min_stat; + gss_buffer_desc request_buf; + + ctx = malloc(sizeof(struct gssctx)); + request_buf.value = malloc(strlen(hostname)+100); + sprintf(request_buf.value, "%s@%s", prot, hostname); + request_buf.length = strlen(request_buf.value) + 1; + maj_stat = gss_import_name(&min_stat, &request_buf, GSS_C_NT_HOSTBASED_SERVICE, + &ctx->target_name); + free(request_buf.value); + if (maj_stat != GSS_S_COMPLETE) { + free(ctx); + ctx = NULL; + DPRINT(Debug,11,(&Debug, + "gssapi_setup: Unable to get service name for %s\n", request_buf.value)); + return 0; + } + ctx->context = GSS_C_NO_CONTEXT; + ctx->username = safe_strdup(username); + ctx->state = state_begin; + return 1; +} + +int gssapi_finish() { + OM_uint32 min_stat; + gss_buffer_desc tok; + + gss_delete_sec_context(&min_stat, &ctx->context, &tok); + gss_release_buffer(&min_stat, &tok); + gss_release_name(&min_stat, ctx->target_name); + free(ctx->username); + free(ctx); + return 1; +} + +int gssapi_continue_handler(char* input, char** output) { + OM_uint32 maj_stat, min_stat; + gss_buffer_desc send_token, request_buf; + gss_buffer_t sec_token; + int cflags; + gss_qop_t quality; + unsigned long buf_size; + + *output = NULL; + request_buf.value = NULL; + switch (ctx->state) { + case state_begin: + sec_token = GSS_C_NO_BUFFER; + break; + case state_continue: + request_buf.value = malloc(strlen(input)); + fromb64(&request_buf, (unsigned char*)input); + sec_token = &request_buf; + break; + case state_end: + request_buf.value = malloc(strlen(input)); + fromb64(&request_buf, (unsigned char*)input); + maj_stat = gss_unwrap(&min_stat, ctx->context, &request_buf, &send_token, &cflags, &quality); + free(request_buf.value); + if (maj_stat != GSS_S_COMPLETE) { + DPRINT(Debug,2,(&Debug, + "gssapi_continue_handler: Cannot unwrap security level data\n")); + return 0; + } + if ( !(((char *)send_token.value)[0] & GSSAUTH_P_NONE) ) { + DPRINT(Debug,2,(&Debug, + "gssapi_continue_handler: Server requires integrity and/or privacy\n")); + return 0; + } + ((char *)send_token.value)[0] = 0; + buf_size = *(long *)send_token.value; + gss_release_buffer(&min_stat, &send_token); + + request_buf.value = malloc(strlen(ctx->username)+6); + memcpy(request_buf.value, &buf_size, 4); + ((char*)request_buf.value)[0] = GSSAUTH_P_NONE; + strcpy(((char*)request_buf.value)+4, ctx->username); + request_buf.length = 4 + strlen(ctx->username) + 1; + maj_stat = gss_wrap(&min_stat, ctx->context, 0, GSS_C_QOP_DEFAULT, &request_buf, &cflags, &send_token); + free(request_buf.value); + if (maj_stat != GSS_S_COMPLETE) { + DPRINT(Debug,2,(&Debug, + "gssapi_continue_handler: Cannot create security level request\n")); + return 0; + } + *output = malloc(send_token.length*4+3); +bzero(*output, send_token.length*4+3); + tob64((unsigned char*)*output, &send_token); + gss_release_buffer(&min_stat, &send_token); + + return 1; + } + + send_token.length = 0; + send_token.value = NULL; + maj_stat = gss_init_sec_context(&min_stat, + GSS_C_NO_CREDENTIAL, + &ctx->context, + ctx->target_name, + GSS_C_NO_OID, + GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + sec_token, + NULL, + &send_token, + NULL, + NULL); + if (request_buf.value) free(request_buf.value); + + if (maj_stat == GSS_S_COMPLETE) { + ctx->state = state_end; + } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { + ctx->state = state_continue; + } else { + DPRINT(Debug,11,(&Debug, + "gssapi_continue_handler: Error exchanging credentials\n")); + return 0; + } + + *output = malloc(send_token.length*4+3); +bzero(*output, send_token.length*4+3); + tob64((unsigned char*)*output, &send_token); + gss_release_buffer(&min_stat, &send_token); + return 1; +} + +#endif diff -urN elm2.4.ME+.124~/lib/mbox/imap.c elm2.4.ME+.124/lib/mbox/imap.c --- elm2.4.ME+.124~/lib/mbox/imap.c 2006-07-05 13:21:50.000000000 -0400 +++ elm2.4.ME+.124/lib/mbox/imap.c 2008-12-23 22:53:34.074232000 -0500 @@ -11,6 +11,7 @@ #include "ss_imp.h" #include "s_me.h" #include "s_elm.h" +#include "gss.h" DEBUG_VAR(Debug,__FILE__,"imap"); @@ -88,6 +89,7 @@ continue_literal } literal_mode; char * current_NOOP_tag ; /* TAG for NOOP command .... */ + f_continue_handler *continue_handler; }; static void cache_zero_imap P_((struct connection_cache *c)); @@ -165,12 +167,14 @@ }; #define CAPA_IMAP4rev1 1 +#define CAPA_GSSAPI 2 static struct capab { char * capa; long mask; } capabilities[] = { - { "IMAP4rev1", CAPA_IMAP4rev1 } + { "IMAP4rev1", CAPA_IMAP4rev1 }, + { "AUTH=GSSAPI", CAPA_GSSAPI } }; #define IMAP_Seen 1 @@ -1420,7 +1424,7 @@ "No imap connection",0); } - if (M_a->read_tokens.token_count < 2) { + if (M_a->read_tokens.token_count < 2 && (M_a->read_tokens.token_count == 0 || M_a->read_tokens.tokens[0].imap_token != imap_continue)) { DPRINT(Debug,1,(&Debug, "parse_response: token_count=%d\n", M_a->read_tokens.token_count)); lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap, @@ -1905,6 +1909,20 @@ case imap_continue: /* TODO: Handle AUTHENTICATE */ + if (M_a->continue_handler) { + char* buf = NULL; + if (!M_a->continue_handler(M_a->read_tokens.tokens[1].str, &buf)) { + buf = safe_strdup("\r\n"); + add_to_write_buffer(M_a, &buf); + return 2; + } + if (buf != NULL) { + add_to_write_buffer(M_a, &buf); /* may free() buf */ + return 2; + } else { + return 1; + } + } if (M_a->literal_mode != waiting_literal_continue) { DPRINT(Debug,1,(&Debug, "parse_response: Unexpected continuation request\n")); @@ -2652,6 +2670,8 @@ con->a.imap_con->literal_mode = no_literal; con->a.imap_con->current_NOOP_tag = NULL; + + con->a.imap_con->continue_handler = NULL; } static void cache_imap_clear_buffers P_((struct connection_cache *con)); @@ -2891,6 +2911,40 @@ StreamInfo(con -> C.stream,SS_ssf,&bits,NULL); +#ifdef GSSAPI + if (con->a.imap_con->capability_bits & CAPA_GSSAPI) { + if (!gssapi_setup("imap", con->C.host, con->C.username)) { + imap_clear_command(con); + goto plainlogin; + } + lib_transient(CATGETS(elm_msg_cat, MeSet,MeIMAPLogin, + "IMAP GSSAPI login to %s as %s ..."), + con->C.host, + con->C.username); + + con->a.imap_con->continue_handler = gssapi_continue_handler; + if (!start_imap_command(con,"AUTHENTICATE GSSAPI")) { + imap_clear_command(con); + gssapi_finish(); + goto plainlogin; + } + end_imap_command(con); + if (imap_command_ok(con, &res, NULL)) { + gssapi_finish(); + con->state = CON_logged; + status = 1; + lib_transient(CATGETS(elm_msg_cat, MeSet,MeIMAPLoginOK, + "IMAP GSSAPI login to %s as %s ... OK"), + con->C.host, + con->C.username); + goto clean; + } + gssapi_finish(); + } +#endif + +plainlogin: + con->a.imap_con->continue_handler = NULL; if (!start_imap_command(con,"LOGIN")) goto clean; @@ -2951,6 +3005,8 @@ con->C.username); clean: + con->a.imap_con->continue_handler = NULL; + imap_clear_command(con); if (data1) { diff -urN elm2.4.ME+.124~/lib/mbox/pop.c elm2.4.ME+.124/lib/mbox/pop.c --- elm2.4.ME+.124~/lib/mbox/pop.c 2006-07-05 13:21:50.000000000 -0400 +++ elm2.4.ME+.124/lib/mbox/pop.c 2008-12-23 22:53:34.078984400 -0500 @@ -11,6 +11,7 @@ #include "ss_imp.h" #include "s_me.h" #include "s_elm.h" +#include "gss.h" DEBUG_VAR(Debug,__FILE__,"pop"); @@ -37,6 +38,12 @@ unsigned int changed : 1; /* == status not on disk */ }; +static f_continue_handler *pop_continue_handler = NULL; + +static int pop_write_stream P_((struct streamsched * ss,void * data)); +static int pop_read_stream P_((struct streamsched *ss,void * data)); +static int pop_idle_timer P_((struct streamsched *ss,void * data)); + static void destroy_uidl_tree P_((struct uidl_entry **root)); static void destroy_uidl_tree(root) struct uidl_entry **root; @@ -256,7 +263,6 @@ } -static int pop_read_stream P_((struct streamsched *ss,void * data)); static int pop_read_stream (ss,data) struct streamsched *ss; void * data; @@ -326,20 +332,55 @@ "pop_read_stream: Wanting response\n")); linelen = find_crlf(&(folder -> p->a.pop_mbx.read_buffer),1); - if (!linelen) - goto check_read; folder -> p->a.pop_mbx.command_status = safe_realloc(folder -> p->a.pop_mbx.command_status, - linelen); - memcpy(folder -> p->a.pop_mbx.command_status, - folder -> p->a.pop_mbx.read_buffer.read_buffer,linelen); - cut_line(&(folder -> p->a.pop_mbx.read_buffer),linelen); - + (linelen>0)?linelen:1); + if (linelen > 0) { + memcpy(folder -> p->a.pop_mbx.command_status, + folder -> p->a.pop_mbx.read_buffer.read_buffer,linelen); + cut_line(&(folder -> p->a.pop_mbx.read_buffer),linelen); + } else { + folder -> p->a.pop_mbx.command_status[0] == '\0'; + } + DPRINT(Debug,13,(&Debug, "pop_read_stream: response=%s\n", folder -> p->a.pop_mbx.command_status)); + if (pop_continue_handler && + folder->p->a.pop_mbx.read_buffer.read_buffer[0] == '+' && + folder->p->a.pop_mbx.read_buffer.read_buffer[1] == ' ') { + char* buf = NULL; + if (!pop_continue_handler(folder->p->a.pop_mbx.command_status, &buf)) { + folder->p->a.pop_mbx.write_buffer.write_buffer = + safe_realloc(folder->p->a.pop_mbx.write_buffer.write_buffer,2); + memcpy(folder->p->a.pop_mbx.write_buffer.write_buffer,"\r\n",2); + folder->p->a.pop_mbx.write_buffer.write_len = 2; + ConfigStream(folder -> p->a.pop_mbx.C.stream, + pop_read_stream,pop_write_stream, + pop_idle_timer,POP_TIMEOUT,folder); + folder->p->a.pop_mbx.pop_state = POP_simple_command; + goto check_read; + } + if (buf != NULL) { + folder->p->a.pop_mbx.write_buffer.write_buffer = + safe_realloc(folder->p->a.pop_mbx.write_buffer.write_buffer,strlen(buf)); + memcpy(folder->p->a.pop_mbx.write_buffer.write_buffer,buf,strlen(buf)); + folder->p->a.pop_mbx.write_buffer.write_len = strlen(buf); + ConfigStream(folder -> p->a.pop_mbx.C.stream, + pop_read_stream,pop_write_stream, + pop_idle_timer,POP_TIMEOUT,folder); + folder->p->a.pop_mbx.pop_state = POP_simple_command; + goto check_read; + } else { + goto check_read; + } + } + + if (!linelen) + goto check_read; + if (folder -> p->a.pop_mbx.command_data) { free(folder -> p->a.pop_mbx.command_data); folder -> p->a.pop_mbx.command_data = NULL; @@ -463,7 +504,6 @@ static int pop_command_ok P_((struct folder_info *folder)); static void pop_clear_command P_((struct folder_info *folder)); -static int pop_idle_timer P_((struct streamsched *ss,void * data)); static int pop_idle_timer (ss,data) struct streamsched *ss; void * data; @@ -510,7 +550,6 @@ return status; } -static int pop_write_stream P_((struct streamsched * ss,void * data)); static int pop_write_stream (ss,data) struct streamsched *ss; void * data; @@ -975,11 +1014,13 @@ int status = 0; int bits; char *data, *data1; + int GSS_found = 0; DPRINT(Debug,12,(&Debug, "pop_open_connection: folder=%p (%s)\n", folder,folder->cur_folder_sys)); + pop_continue_handler = NULL; /* Clear buffer from old data ... */ pop_clear_buffers(folder); @@ -1079,6 +1120,9 @@ if (0 == strcmp(capa,"TOP")) TOP_found ++; + if (0 == strcmp(capa,"SASL") && arg && strstr(arg, "GSSAPI")) + /* XXX pedantic: check spaces around it */ + GSS_found ++; #ifdef USE_DLOPEN probe_pop_capa_lib(&pop_capa_libs, @@ -1136,6 +1180,36 @@ StreamInfo(folder -> p->a.pop_mbx.C.stream,SS_ssf,&bits,NULL); +#ifdef GSSAPI + if (GSS_found) { + if (!gssapi_setup("pop", folder -> p->a.pop_mbx.C.host, folder -> p->a.pop_mbx.C.username)) { + pop_clear_command(folder); + goto plainlogin; + } + lib_transient(CATGETS(elm_msg_cat, MeSet,MePOPLoginClear, + "POP GSSAPI login to %s as %s ..."), + folder -> p->a.pop_mbx.C.host, + folder -> p->a.pop_mbx.C.username); + pop_continue_handler = gssapi_continue_handler; + data = elm_message(FRM("AUTH GSSAPI")); + pop_push_command(folder,data,1); + free(data); + if (pop_command_ok(folder)) { + gssapi_finish(); + lib_transient(CATGETS(elm_msg_cat, MeSet,MePOPLoginOK, + "POP login to %s as %s ... OK"), + folder -> p->a.pop_mbx.C.host, + folder -> p->a.pop_mbx.C.username); + pop_clear_command(folder); + status = 1; + goto clean; + } + gssapi_finish(); + pop_clear_command(folder); + } +#endif +plainlogin: + pop_continue_handler = NULL; data = elm_message(FRM("USER %s"), folder -> p->a.pop_mbx.C.username); @@ -1190,6 +1264,7 @@ folder -> p->a.pop_mbx.C.username); clean: + pop_continue_handler = NULL; pop_clear_command(folder); if (!status) {