static int smtp_open (CONNECTION* conn) { int rc; if (mutt_socket_open (conn)) return -1; /* get greeting string */ if ((rc = smtp_get_resp (conn))) return rc; if ((rc = smtp_helo (conn))) return rc; #ifdef USE_SSL if (conn->ssf) rc = M_NO; else if (option (OPTSSLFORCETLS)) rc = M_YES; else if (mutt_bit_isset (Capabilities, STARTTLS) && (rc = query_quadoption (OPT_SSLSTARTTLS, _("Secure connection with TLS?"))) == -1) return rc; if (rc == M_YES) { if (mutt_socket_write (conn, "STARTTLS\r\n") < 0) return smtp_err_write; if ((rc = smtp_get_resp (conn))) return rc; if (mutt_ssl_starttls (conn)) { mutt_error (_("Could not negotiate TLS connection")); mutt_sleep (1); return -1; } /* re-EHLO to get authentication mechanisms */ if ((rc = smtp_helo (conn))) return rc; } #endif if (conn->account.flags & M_ACCT_USER) { if (!mutt_bit_isset (Capabilities, AUTH)) { mutt_error (_("SMTP server does not support authentication")); mutt_sleep (1); return -1; } #ifdef USE_SASL return smtp_auth (conn); #else mutt_error (_("SMTP authentication requires SASL")); mutt_sleep (1); return -1; #endif /* USE_SASL */ } return 0; }
static int smtp_helo (CONNECTION* conn) { char buf[LONG_STRING]; const char* fqdn; memset (Capabilities, 0, sizeof (Capabilities)); if (!Esmtp) { /* if TLS or AUTH are requested, use EHLO */ if (conn->account.flags & M_ACCT_USER) Esmtp = 1; #ifdef USE_SSL if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != M_NO) Esmtp = 1; #endif } if(!(fqdn = mutt_fqdn (0))) fqdn = NONULL (Hostname); snprintf (buf, sizeof (buf), "%s %s\r\n", Esmtp ? "EHLO" : "HELO", fqdn); /* XXX there should probably be a wrapper in mutt_socket.c that * repeatedly calls conn->write until all data is sent. This * currently doesn't check for a short write. */ if (mutt_socket_write (conn, buf) == -1) return smtp_err_write; return smtp_get_resp (conn); }
static int smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a) { char buf[1024]; int r; while (a) { /* weed out group mailboxes, since those are for display only */ if (!a->mailbox || a->group) { a = a->next; continue; } if (mutt_bit_isset (Capabilities, DSN) && DsnNotify) snprintf (buf, sizeof (buf), "RCPT TO:<%s> NOTIFY=%s\r\n", a->mailbox, DsnNotify); else snprintf (buf, sizeof (buf), "RCPT TO:<%s>\r\n", a->mailbox); if (mutt_socket_write (conn, buf) == -1) return smtp_err_write; if ((r = smtp_get_resp (conn))) return r; a = a->next; } return 0; }
/* this is basically a stripped-down version of the cram-md5 method. */ imap_auth_res_t imap_auth_anon (IMAP_DATA* idata, const char* method) { int rc; if (!mutt_bit_isset (idata->capabilities, AUTH_ANON)) return IMAP_AUTH_UNAVAIL; if (mutt_account_getuser (&idata->conn->account)) return IMAP_AUTH_FAILURE; if (idata->conn->account.user[0] != '\0') return IMAP_AUTH_UNAVAIL; mutt_message _("Authenticating (anonymous)..."); imap_cmd_start (idata, "AUTHENTICATE ANONYMOUS"); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { dprint (1, (debugfile, "Invalid response from server.\n")); goto bail; } mutt_socket_write (idata->conn, "ZHVtbXkK\r\n"); /* base64 ("dummy") */ do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_OK) { dprint (1, (debugfile, "Error receiving server response.\n")); goto bail; } if (imap_code (idata->buf)) return IMAP_AUTH_SUCCESS; bail: mutt_error _("Anonymous authentication failed."); mutt_sleep (2); return IMAP_AUTH_FAILURE; }
/* imap_auth_sasl: Default authenticator if available. */ imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata, const char* method) { sasl_conn_t* saslconn; sasl_interact_t* interaction = NULL; int rc, irc; char buf[HUGE_STRING]; const char* mech; const char *pc = NULL; unsigned int len, olen; unsigned char client_start; if (mutt_sasl_client_new (idata->conn, &saslconn) < 0) { dprint (1, (debugfile, "imap_auth_sasl: Error allocating SASL connection.\n")); return IMAP_AUTH_FAILURE; } rc = SASL_FAIL; /* If the user hasn't specified a method, use any available */ if (!method) { method = idata->capstr; /* hack for SASL ANONYMOUS support: * 1. Fetch username. If it's "" or "anonymous" then * 2. attempt sasl_client_start with only "AUTH=ANONYMOUS" capability * 3. if sasl_client_start fails, fall through... */ if (mutt_account_getuser (&idata->conn->account)) return IMAP_AUTH_FAILURE; if (mutt_bit_isset (idata->capabilities, AUTH_ANON) && (!idata->conn->account.user[0] || !ascii_strncmp (idata->conn->account.user, "anonymous", 9))) rc = sasl_client_start (saslconn, "AUTH=ANONYMOUS", NULL, &pc, &olen, &mech); } else if (!ascii_strcasecmp ("login", method) && !strstr (NONULL (idata->capstr), "AUTH=LOGIN")) /* do not use SASL login for regular IMAP login (#3556) */ return IMAP_AUTH_UNAVAIL; if (rc != SASL_OK && rc != SASL_CONTINUE) do { rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen, &mech); if (rc == SASL_INTERACT) mutt_sasl_interact (interaction); } while (rc == SASL_INTERACT); client_start = (olen > 0); if (rc != SASL_OK && rc != SASL_CONTINUE) { if (method) dprint (2, (debugfile, "imap_auth_sasl: %s unavailable\n", method)); else dprint (1, (debugfile, "imap_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n")); /* SASL doesn't support LOGIN, so fall back */ return IMAP_AUTH_UNAVAIL; } mutt_message (_("Authenticating (%s)..."), mech); snprintf (buf, sizeof (buf), "AUTHENTICATE %s", mech); if (mutt_bit_isset (idata->capabilities, SASL_IR) && client_start) { len = mutt_strlen (buf); buf[len++] = ' '; if (sasl_encode64 (pc, olen, buf + len, sizeof (buf) - len, &olen) != SASL_OK) { dprint (1, (debugfile, "imap_auth_sasl: error base64-encoding client response.\n")); goto bail; } client_start = olen = 0; } imap_cmd_start (idata, buf); irc = IMAP_CMD_CONTINUE; /* looping protocol */ while (rc == SASL_CONTINUE || olen > 0) { do irc = imap_cmd_step (idata); while (irc == IMAP_CMD_CONTINUE); if (irc == IMAP_CMD_BAD || irc == IMAP_CMD_NO) goto bail; if (irc == IMAP_CMD_RESPOND) { /* Exchange incorrectly returns +\r\n instead of + \r\n */ if (idata->buf[1] == '\0') { buf[0] = '\0'; len = 0; } else if (sasl_decode64 (idata->buf+2, strlen (idata->buf+2), buf, LONG_STRING-1, &len) != SASL_OK) { dprint (1, (debugfile, "imap_auth_sasl: error base64-decoding server response.\n")); goto bail; } } /* client-start is only available with the SASL-IR extension, but * SASL 2.1 seems to want to use it regardless, at least for DIGEST * fast reauth. Override if the server sent an initial continuation */ if (!client_start || buf[0]) { do { rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); if (rc == SASL_INTERACT) mutt_sasl_interact (interaction); } while (rc == SASL_INTERACT); } else client_start = 0; /* send out response, or line break if none needed */ if (olen) { if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) { dprint (1, (debugfile, "imap_auth_sasl: error base64-encoding client response.\n")); goto bail; } } if (irc == IMAP_CMD_RESPOND) { strfcpy (buf + olen, "\r\n", sizeof (buf) - olen); mutt_socket_write (idata->conn, buf); } /* If SASL has errored out, send an abort string to the server */ if (rc < 0) { mutt_socket_write (idata->conn, "*\r\n"); dprint (1, (debugfile, "imap_auth_sasl: sasl_client_step error %d\n",rc)); } olen = 0; } while (irc != IMAP_CMD_OK) if ((irc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) break; if (rc != SASL_OK) goto bail; if (imap_code (idata->buf)) { mutt_sasl_setup_conn (idata->conn, saslconn); return IMAP_AUTH_SUCCESS; } bail: sasl_dispose (&saslconn); if (method) { dprint (2, (debugfile, "imap_auth_sasl: %s failed\n", method)); return IMAP_AUTH_UNAVAIL; } mutt_error _("SASL authentication failed."); mutt_sleep(2); return IMAP_AUTH_FAILURE; }
int imap_append_message (CONTEXT *ctx, MESSAGE *msg) { IMAP_DATA* idata; FILE *fp; char buf[LONG_STRING]; char mbox[LONG_STRING]; char mailbox[LONG_STRING]; char internaldate[IMAP_DATELEN]; char imap_flags[SHORT_STRING]; size_t len; progress_t progressbar; size_t sent; int c, last; IMAP_MBOX mx; int rc; idata = (IMAP_DATA*) ctx->data; if (imap_parse_path (ctx->path, &mx)) return -1; imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); if (!*mailbox) strfcpy (mailbox, "INBOX", sizeof (mailbox)); if ((fp = fopen (msg->path, "r")) == NULL) { mutt_perror (msg->path); goto fail; } /* currently we set the \Seen flag on all messages, but probably we * should scan the message Status header for flag info. Since we're * already rereading the whole file for length it isn't any more * expensive (it'd be nice if we had the file size passed in already * by the code that writes the file, but that's a lot of changes. * Ideally we'd have a HEADER structure with flag info here... */ for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c) { if(c == '\n' && last != '\r') len++; len++; } rewind (fp); mutt_progress_init (&progressbar, _("Uploading message..."), MUTT_PROGRESS_SIZE, NetInc, len); imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); imap_make_date (internaldate, msg->received); imap_flags[0] = imap_flags[1] = 0; if (msg->flags.read) safe_strcat (imap_flags, sizeof (imap_flags), " \\Seen"); if (msg->flags.replied) safe_strcat (imap_flags, sizeof (imap_flags), " \\Answered"); if (msg->flags.flagged) safe_strcat (imap_flags, sizeof (imap_flags), " \\Flagged"); if (msg->flags.draft) safe_strcat (imap_flags, sizeof (imap_flags), " \\Draft"); snprintf (buf, sizeof (buf), "APPEND %s (%s) \"%s\" {%lu}", mbox, imap_flags + 1, internaldate, (unsigned long) len); imap_cmd_start (idata, buf); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { char *pc; dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", idata->buf)); pc = idata->buf + SEQLEN; SKIPWS (pc); pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); safe_fclose (&fp); goto fail; } for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c) { if (c == '\n' && last != '\r') buf[len++] = '\r'; buf[len++] = c; if (len > sizeof(buf) - 3) { sent += len; flush_buffer(buf, &len, idata->conn); mutt_progress_update (&progressbar, sent, -1); } } if (len) flush_buffer(buf, &len, idata->conn); mutt_socket_write (idata->conn, "\r\n"); safe_fclose (&fp); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (!imap_code (idata->buf)) { char *pc; dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", idata->buf)); pc = idata->buf + SEQLEN; SKIPWS (pc); pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); goto fail; } FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }
static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist) { sasl_conn_t* saslconn; sasl_interact_t* interaction = NULL; const char* mech; const char* data = NULL; unsigned int len; char buf[HUGE_STRING]; int rc, saslrc; if (mutt_sasl_client_new (conn, &saslconn) < 0) return SMTP_AUTH_FAIL; do { rc = sasl_client_start (saslconn, mechlist, &interaction, &data, &len, &mech); if (rc == SASL_INTERACT) mutt_sasl_interact (interaction); } while (rc == SASL_INTERACT); if (rc != SASL_OK && rc != SASL_CONTINUE) { dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", mech)); sasl_dispose (&saslconn); return SMTP_AUTH_UNAVAIL; } if (!option(OPTNOCURSES)) mutt_message (_("Authenticating (%s)..."), mech); snprintf (buf, sizeof (buf), "AUTH %s", mech); if (len) { safe_strcat (buf, sizeof (buf), " "); if (sasl_encode64 (data, len, buf + mutt_strlen (buf), sizeof (buf) - mutt_strlen (buf), &len) != SASL_OK) { dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n")); goto fail; } } safe_strcat (buf, sizeof (buf), "\r\n"); do { if (mutt_socket_write (conn, buf) < 0) goto fail; if ((rc = mutt_socket_readln (buf, sizeof (buf), conn)) < 0) goto fail; if (smtp_code (buf, rc, &rc) < 0) goto fail; if (rc != smtp_ready) break; if (sasl_decode64 (buf+4, strlen (buf+4), buf, sizeof (buf), &len) != SASL_OK) { dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n")); goto fail; } do { saslrc = sasl_client_step (saslconn, buf, len, &interaction, &data, &len); if (saslrc == SASL_INTERACT) mutt_sasl_interact (interaction); } while (saslrc == SASL_INTERACT); if (len) { if (sasl_encode64 (data, len, buf, sizeof (buf), &len) != SASL_OK) { dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n")); goto fail; } } strfcpy (buf + len, "\r\n", sizeof (buf) - len); } while (rc == smtp_ready && saslrc != SASL_FAIL); if (smtp_success (rc)) { mutt_sasl_setup_conn (conn, saslconn); return SMTP_AUTH_SUCCESS; } fail: sasl_dispose (&saslconn); return SMTP_AUTH_FAIL; }
int mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc, const ADDRESS* bcc, const char *msgfile, int eightbit) { CONNECTION *conn; ACCOUNT account; const char* envfrom; char buf[1024]; int ret = -1; /* it might be better to synthesize an envelope from from user and host * but this condition is most likely arrived at accidentally */ if (EnvFrom) envfrom = EnvFrom->mailbox; else if (from) envfrom = from->mailbox; else { mutt_error (_("No from address given")); return -1; } if (smtp_fill_account (&account) < 0) return ret; if (!(conn = mutt_conn_find (NULL, &account))) return -1; Esmtp = eightbit; do { /* send our greeting */ if (( ret = smtp_open (conn))) break; FREE (&AuthMechs); /* send the sender's address */ ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom); if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME)) { safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15); ret += 14; } if (DsnReturn && mutt_bit_isset (Capabilities, DSN)) ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn); safe_strncat (buf, sizeof (buf), "\r\n", 3); if (mutt_socket_write (conn, buf) == -1) { ret = smtp_err_write; break; } if ((ret = smtp_get_resp (conn))) break; /* send the recipient list */ if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc)) || (ret = smtp_rcpt_to (conn, bcc))) break; /* send the message data */ if ((ret = smtp_data (conn, msgfile))) break; mutt_socket_write (conn, "QUIT\r\n"); ret = 0; } while (0); if (conn) mutt_socket_close (conn); if (ret == smtp_err_read) mutt_error (_("SMTP session failed: read error")); else if (ret == smtp_err_write) mutt_error (_("SMTP session failed: write error")); else if (ret == smtp_err_code) mutt_error (_("Invalid server response")); return ret; }
static int smtp_data (CONNECTION * conn, const char *msgfile) { char buf[1024]; FILE *fp = 0; progress_t progress; struct stat st; int r, term = 0; size_t buflen = 0; fp = fopen (msgfile, "r"); if (!fp) { mutt_error (_("SMTP session failed: unable to open %s"), msgfile); return -1; } stat (msgfile, &st); unlink (msgfile); mutt_progress_init (&progress, _("Sending message..."), M_PROGRESS_SIZE, NetInc, st.st_size); snprintf (buf, sizeof (buf), "DATA\r\n"); if (mutt_socket_write (conn, buf) == -1) { safe_fclose (&fp); return smtp_err_write; } if ((r = smtp_get_resp (conn))) { safe_fclose (&fp); return r; } while (fgets (buf, sizeof (buf) - 1, fp)) { buflen = mutt_strlen (buf); term = buf[buflen-1] == '\n'; if (buflen && buf[buflen-1] == '\n' && (buflen == 1 || buf[buflen - 2] != '\r')) snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n"); if (buf[0] == '.') { if (mutt_socket_write_d (conn, ".", -1, M_SOCK_LOG_FULL) == -1) { safe_fclose (&fp); return smtp_err_write; } } if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) == -1) { safe_fclose (&fp); return smtp_err_write; } mutt_progress_update (&progress, ftell (fp), -1); } if (!term && buflen && mutt_socket_write_d (conn, "\r\n", -1, M_SOCK_LOG_FULL) == -1) { safe_fclose (&fp); return smtp_err_write; } safe_fclose (&fp); /* terminate the message body */ if (mutt_socket_write (conn, ".\r\n") == -1) return smtp_err_write; if ((r = smtp_get_resp (conn))) return r; return 0; }
/* imap_auth_gss: AUTH=GSSAPI support. */ imap_auth_res_t imap_auth_gss (IMAP_DATA* idata, const char* method) { gss_buffer_desc request_buf, send_token; gss_buffer_t sec_token; gss_name_t target_name; gss_ctx_id_t context; #ifdef DEBUG gss_OID mech_name; #endif gss_qop_t quality; int cflags; OM_uint32 maj_stat, min_stat; char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags; unsigned long buf_size; int rc; if (!mutt_bit_isset (idata->capabilities, AGSSAPI)) return IMAP_AUTH_UNAVAIL; if (mutt_account_getuser (&idata->conn->account)) return IMAP_AUTH_FAILURE; /* get an IMAP service ticket for the server */ snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->account.host); request_buf.value = buf1; request_buf.length = strlen (buf1) + 1; maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name, &target_name); if (maj_stat != GSS_S_COMPLETE) { dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1)); return IMAP_AUTH_UNAVAIL; } #ifdef DEBUG else if (debuglevel >= 2) { maj_stat = gss_display_name (&min_stat, target_name, &request_buf, &mech_name); dprint (2, (debugfile, "Using service name [%s]\n", (char*) request_buf.value)); maj_stat = gss_release_buffer (&min_stat, &request_buf); } #endif /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */ sec_token = GSS_C_NO_BUFFER; context = GSS_C_NO_CONTEXT; /* build token */ maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context, 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, (unsigned int*) &cflags, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { print_gss_error(maj_stat, min_stat); dprint (1, (debugfile, "Error acquiring credentials - no TGT?\n")); gss_release_name (&min_stat, &target_name); return IMAP_AUTH_UNAVAIL; } /* now begin login */ mutt_message _("Authenticating (GSSAPI)..."); imap_cmd_start (idata, "AUTHENTICATE GSSAPI"); /* expect a null continuation response ("+") */ do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { dprint (2, (debugfile, "Invalid response from server: %s\n", buf1)); gss_release_name (&min_stat, &target_name); goto bail; } /* now start the security context initialisation loop... */ dprint (2, (debugfile, "Sending credentials\n")); mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length, sizeof (buf1) - 2); gss_release_buffer (&min_stat, &send_token); safe_strcat (buf1, sizeof (buf1), "\r\n"); mutt_socket_write (idata->conn, buf1); while (maj_stat == GSS_S_CONTINUE_NEEDED) { /* Read server data */ do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { dprint (1, (debugfile, "Error receiving server response.\n")); gss_release_name (&min_stat, &target_name); goto bail; } request_buf.length = mutt_from_base64 (buf2, idata->buf + 2); request_buf.value = buf2; sec_token = &request_buf; /* Write client data */ maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context, 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, (unsigned int*) &cflags, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { print_gss_error(maj_stat, min_stat); dprint (1, (debugfile, "Error exchanging credentials\n")); gss_release_name (&min_stat, &target_name); goto err_abort_cmd; } mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length, sizeof (buf1) - 2); gss_release_buffer (&min_stat, &send_token); safe_strcat (buf1, sizeof (buf1), "\r\n"); mutt_socket_write (idata->conn, buf1); } gss_release_name (&min_stat, &target_name); /* get security flags and buffer size */ do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { dprint (1, (debugfile, "Error receiving server response.\n")); goto bail; } request_buf.length = mutt_from_base64 (buf2, idata->buf + 2); request_buf.value = buf2; maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token, &cflags, &quality); if (maj_stat != GSS_S_COMPLETE) { print_gss_error(maj_stat, min_stat); dprint (2, (debugfile, "Couldn't unwrap security level data\n")); gss_release_buffer (&min_stat, &send_token); goto err_abort_cmd; } dprint (2, (debugfile, "Credential exchange complete\n")); /* first octet is security levels supported. We want NONE */ server_conf_flags = ((char*) send_token.value)[0]; if ( !(((char*) send_token.value)[0] & GSS_AUTH_P_NONE) ) { dprint (2, (debugfile, "Server requires integrity or privacy\n")); gss_release_buffer (&min_stat, &send_token); goto err_abort_cmd; } /* we don't care about buffer size if we don't wrap content. But here it is */ ((char*) send_token.value)[0] = 0; buf_size = ntohl (*((long *) send_token.value)); gss_release_buffer (&min_stat, &send_token); dprint (2, (debugfile, "Unwrapped security level flags: %c%c%c\n", server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-', server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-', server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-')); dprint (2, (debugfile, "Maximum GSS token size is %ld\n", buf_size)); /* agree to terms (hack!) */ buf_size = htonl (buf_size); /* not relevant without integrity/privacy */ memcpy (buf1, &buf_size, 4); buf1[0] = GSS_AUTH_P_NONE; /* server decides if principal can log in as user */ strncpy (buf1 + 4, idata->conn->account.user, sizeof (buf1) - 4); request_buf.value = buf1; request_buf.length = 4 + strlen (idata->conn->account.user) + 1; maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf, &cflags, &send_token); if (maj_stat != GSS_S_COMPLETE) { dprint (2, (debugfile, "Error creating login request\n")); goto err_abort_cmd; } mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length, sizeof (buf1) - 2); dprint (2, (debugfile, "Requesting authorisation as %s\n", idata->conn->account.user)); safe_strcat (buf1, sizeof (buf1), "\r\n"); mutt_socket_write (idata->conn, buf1); /* Joy of victory or agony of defeat? */ do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc == IMAP_CMD_RESPOND) { dprint (1, (debugfile, "Unexpected server continuation request.\n")); goto err_abort_cmd; } if (imap_code (idata->buf)) { /* flush the security context */ dprint (2, (debugfile, "Releasing GSS credentials\n")); maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token); if (maj_stat != GSS_S_COMPLETE) dprint (1, (debugfile, "Error releasing credentials\n")); /* send_token may contain a notification to the server to flush * credentials. RFC 1731 doesn't specify what to do, and since this * support is only for authentication, we'll assume the server knows * enough to flush its own credentials */ gss_release_buffer (&min_stat, &send_token); return IMAP_AUTH_SUCCESS; } else goto bail; err_abort_cmd: mutt_socket_write (idata->conn, "*\r\n"); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); bail: mutt_error _("GSSAPI authentication failed."); mutt_sleep (2); return IMAP_AUTH_FAILURE; }