/* For AUTH NTLM (without initial response) responses */ static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; char *type1msg = NULL; size_t len = 0; (void)instate; /* no use for this yet */ if(smtpcode != 334) { failf(data, "Access denied: %d", smtpcode); result = CURLE_LOGIN_DENIED; } else { /* Create the type-1 message */ result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, &conn->ntlm, &type1msg, &len); /* Send the message */ if(!result) { if(type1msg) { result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg); if(!result) state(conn, SMTP_AUTH_NTLM_TYPE2MSG); } Curl_safefree(type1msg); } } return result; }
static CURLcode smtp_authenticate(struct connectdata *conn) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; char *initresp = NULL; const char *mech = NULL; size_t len = 0; smtpstate state1 = SMTP_STOP; smtpstate state2 = SMTP_STOP; /* Check we have a username and password to authenticate with and end the connect phase if we don't */ if(!conn->bits.user_passwd) { state(conn, SMTP_STOP); return result; } /* Check supported authentication mechanisms by decreasing order of security */ #ifndef CURL_DISABLE_CRYPTO_AUTH if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) { mech = "DIGEST-MD5"; state1 = SMTP_AUTH_DIGESTMD5; smtpc->authused = SASL_MECH_DIGEST_MD5; } else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) { mech = "CRAM-MD5"; state1 = SMTP_AUTH_CRAMMD5; smtpc->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM if(smtpc->authmechs & SASL_MECH_NTLM) { mech = "NTLM"; state1 = SMTP_AUTH_NTLM; state2 = SMTP_AUTH_NTLM_TYPE2MSG; smtpc->authused = SASL_MECH_NTLM; result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, &conn->ntlm, &initresp, &len); } else #endif if(smtpc->authmechs & SASL_MECH_LOGIN) { mech = "LOGIN"; state1 = SMTP_AUTH_LOGIN; state2 = SMTP_AUTH_PASSWD; smtpc->authused = SASL_MECH_LOGIN; result = Curl_sasl_create_login_message(conn->data, conn->user, &initresp, &len); } else if(smtpc->authmechs & SASL_MECH_PLAIN) { mech = "PLAIN"; state1 = SMTP_AUTH_PLAIN; state2 = SMTP_AUTH; smtpc->authused = SASL_MECH_PLAIN; result = Curl_sasl_create_plain_message(conn->data, conn->user, conn->passwd, &initresp, &len); } else { infof(conn->data, "No known authentication mechanisms supported!\n"); result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */ } if(!result) { if(initresp && strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */ result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); if(!result) state(conn, state2); } else { result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); if(!result) state(conn, state1); } Curl_safefree(initresp); } return result; }
/* * Curl_sasl_continue() * * Continue the authentication. */ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, int code, saslprogress *progress) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; saslstate newstate = SASL_FINAL; char *resp = NULL; #if !defined(CURL_DISABLE_CRYPTO_AUTH) char *serverdata; char *chlg = NULL; size_t chlglen = 0; #endif size_t len = 0; *progress = SASL_INPROGRESS; if(sasl->state == SASL_FINAL) { if(code != sasl->params->finalcode) result = CURLE_LOGIN_DENIED; *progress = SASL_DONE; state(sasl, conn, SASL_STOP); return result; } if(sasl->state != SASL_CANCEL && code != sasl->params->contcode) { *progress = SASL_DONE; state(sasl, conn, SASL_STOP); return CURLE_LOGIN_DENIED; } switch(sasl->state) { case SASL_STOP: *progress = SASL_DONE; return result; case SASL_PLAIN: result = sasl_create_plain_message(data, conn->user, conn->passwd, &resp, &len); break; case SASL_LOGIN: result = sasl_create_login_message(data, conn->user, &resp, &len); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: result = sasl_create_login_message(data, conn->passwd, &resp, &len); break; case SASL_EXTERNAL: result = sasl_create_external_message(data, conn->user, &resp, &len); break; #ifndef CURL_DISABLE_CRYPTO_AUTH case SASL_CRAMMD5: sasl->params->getmessage(data->state.buffer, &serverdata); result = sasl_decode_cram_md5_message(serverdata, &chlg, &chlglen); if(!result) result = sasl_create_cram_md5_message(data, chlg, conn->user, conn->passwd, &resp, &len); free(chlg); break; case SASL_DIGESTMD5: sasl->params->getmessage(data->state.buffer, &serverdata); result = Curl_sasl_create_digest_md5_message(data, serverdata, conn->user, conn->passwd, sasl->params->service, &resp, &len); newstate = SASL_DIGESTMD5_RESP; break; case SASL_DIGESTMD5_RESP: if(!(resp = strdup(""))) result = CURLE_OUT_OF_MEMORY; break; #endif #ifdef USE_NTLM case SASL_NTLM: /* Create the type-1 message */ result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, &conn->ntlm, &resp, &len); newstate = SASL_NTLM_TYPE2MSG; break; case SASL_NTLM_TYPE2MSG: /* Decode the type-2 message */ sasl->params->getmessage(data->state.buffer, &serverdata); result = Curl_sasl_decode_ntlm_type2_message(data, serverdata, &conn->ntlm); if(!result) result = Curl_sasl_create_ntlm_type3_message(data, conn->user, conn->passwd, &conn->ntlm, &resp, &len); break; #endif #if defined(USE_KERBEROS5) case SASL_GSSAPI: result = Curl_sasl_create_gssapi_user_message(data, conn->user, conn->passwd, sasl->params->service, sasl->mutual_auth, NULL, &conn->krb5, &resp, &len); newstate = SASL_GSSAPI_TOKEN; break; case SASL_GSSAPI_TOKEN: sasl->params->getmessage(data->state.buffer, &serverdata); if(sasl->mutual_auth) { /* Decode the user token challenge and create the optional response message */ result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL, sasl->mutual_auth, serverdata, &conn->krb5, &resp, &len); newstate = SASL_GSSAPI_NO_DATA; } else /* Decode the security challenge and create the response message */ result = Curl_sasl_create_gssapi_security_message(data, serverdata, &conn->krb5, &resp, &len); break; case SASL_GSSAPI_NO_DATA: sasl->params->getmessage(data->state.buffer, &serverdata); /* Decode the security challenge and create the response message */ result = Curl_sasl_create_gssapi_security_message(data, serverdata, &conn->krb5, &resp, &len); break; #endif case SASL_XOAUTH2: /* Create the authorisation message */ result = sasl_create_xoauth2_message(data, conn->user, conn->xoauth2_bearer, &resp, &len); break; case SASL_CANCEL: /* Remove the offending mechanism from the supported list */ sasl->authmechs ^= sasl->authused; /* Start an alternative SASL authentication */ result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress); newstate = sasl->state; /* Use state from Curl_sasl_start() */ break; default: failf(data, "Unsupported SASL authentication mechanism"); result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ break; } switch(result) { case CURLE_BAD_CONTENT_ENCODING: /* Cancel dialog */ result = sasl->params->sendcont(conn, "*"); newstate = SASL_CANCEL; break; case CURLE_OK: if(resp) result = sasl->params->sendcont(conn, resp); break; default: newstate = SASL_STOP; /* Stop on error */ *progress = SASL_DONE; break; } free(resp); state(sasl, conn, newstate); return result; }
/* * This is for creating ntlm header output */ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy) { char *base64 = NULL; size_t len = 0; CURLcode result; /* point to the address of the pointer that holds the string to send to the server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; /* point to the name and password for this */ const char *userp; const char *passwdp; /* point to the correct struct with this */ struct ntlmdata *ntlm; struct auth *authp; DEBUGASSERT(conn); DEBUGASSERT(conn->data); #ifdef USE_NSS if(CURLE_OK != Curl_nss_force_init(conn->data)) return CURLE_OUT_OF_MEMORY; #endif if(proxy) { allocuserpwd = &conn->allocptr.proxyuserpwd; userp = conn->proxyuser; passwdp = conn->proxypasswd; ntlm = &conn->proxyntlm; authp = &conn->data->state.authproxy; } else { allocuserpwd = &conn->allocptr.userpwd; userp = conn->user; passwdp = conn->passwd; ntlm = &conn->ntlm; authp = &conn->data->state.authhost; } authp->done = FALSE; /* not set means empty */ if(!userp) userp = ""; if(!passwdp) passwdp = ""; #ifdef USE_WINDOWS_SSPI if(s_hSecDll == NULL) { /* not thread safe and leaks - use curl_global_init() to avoid */ CURLcode err = Curl_sspi_global_init(); if(s_hSecDll == NULL) return err; } #endif switch(ntlm->state) { case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ /* Create a type-1 message */ result = Curl_sasl_create_ntlm_type1_message(userp, passwdp, ntlm, &base64, &len); if(result) return result; if(base64) { free(*allocuserpwd); *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", base64); free(base64); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); } break; case NTLMSTATE_TYPE2: /* We already received the type-2 message, create a type-3 message */ result = Curl_sasl_create_ntlm_type3_message(conn->data, userp, passwdp, ntlm, &base64, &len); if(result) return result; if(base64) { free(*allocuserpwd); *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", proxy ? "Proxy-" : "", base64); free(base64); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); ntlm->state = NTLMSTATE_TYPE3; /* we send a type-3 */ authp->done = TRUE; } break; case NTLMSTATE_TYPE3: /* connection is already authenticated, * don't send a header in future requests */ ntlm->state = NTLMSTATE_LAST; /* fall-through */ case NTLMSTATE_LAST: Curl_safefree(*allocuserpwd); authp->done = TRUE; break; } return CURLE_OK; }
/* * Curl_sasl_start() * * Calculate the required login details for SASL authentication. */ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, bool force_ir, saslprogress *progress) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; unsigned int enabledmechs; const char *mech = NULL; char *resp = NULL; size_t len = 0; saslstate state1 = SASL_STOP; saslstate state2 = SASL_FINAL; sasl->force_ir = force_ir; /* Latch for future use */ sasl->authused = 0; /* No mechanism used yet */ enabledmechs = sasl->authmechs & sasl->prefmech; *progress = SASL_IDLE; /* Calculate the supported authentication mechanism, by decreasing order of security, as well as the initial response where appropriate */ if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { mech = SASL_MECH_STRING_EXTERNAL; state1 = SASL_EXTERNAL; sasl->authused = SASL_MECH_EXTERNAL; if(force_ir || data->set.sasl_ir) result = sasl_create_external_message(data, conn->user, &resp, &len); } else if(conn->bits.user_passwd) { #if defined(USE_KERBEROS5) if(enabledmechs & SASL_MECH_GSSAPI) { sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */ mech = SASL_MECH_STRING_GSSAPI; state1 = SASL_GSSAPI; state2 = SASL_GSSAPI_TOKEN; sasl->authused = SASL_MECH_GSSAPI; if(force_ir || data->set.sasl_ir) result = Curl_sasl_create_gssapi_user_message(data, conn->user, conn->passwd, sasl->params->service, sasl->mutual_auth, NULL, &conn->krb5, &resp, &len); } else #endif #ifndef CURL_DISABLE_CRYPTO_AUTH if(enabledmechs & SASL_MECH_DIGEST_MD5) { mech = SASL_MECH_STRING_DIGEST_MD5; state1 = SASL_DIGESTMD5; sasl->authused = SASL_MECH_DIGEST_MD5; } else if(enabledmechs & SASL_MECH_CRAM_MD5) { mech = SASL_MECH_STRING_CRAM_MD5; state1 = SASL_CRAMMD5; sasl->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM if(enabledmechs & SASL_MECH_NTLM) { mech = SASL_MECH_STRING_NTLM; state1 = SASL_NTLM; state2 = SASL_NTLM_TYPE2MSG; sasl->authused = SASL_MECH_NTLM; if(force_ir || data->set.sasl_ir) result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, &conn->ntlm, &resp, &len); } else #endif if((enabledmechs & SASL_MECH_XOAUTH2) || conn->xoauth2_bearer) { mech = SASL_MECH_STRING_XOAUTH2; state1 = SASL_XOAUTH2; sasl->authused = SASL_MECH_XOAUTH2; if(force_ir || data->set.sasl_ir) result = sasl_create_xoauth2_message(data, conn->user, conn->xoauth2_bearer, &resp, &len); } else if(enabledmechs & SASL_MECH_LOGIN) { mech = SASL_MECH_STRING_LOGIN; state1 = SASL_LOGIN; state2 = SASL_LOGIN_PASSWD; sasl->authused = SASL_MECH_LOGIN; if(force_ir || data->set.sasl_ir) result = sasl_create_login_message(data, conn->user, &resp, &len); } else if(enabledmechs & SASL_MECH_PLAIN) { mech = SASL_MECH_STRING_PLAIN; state1 = SASL_PLAIN; sasl->authused = SASL_MECH_PLAIN; if(force_ir || data->set.sasl_ir) result = sasl_create_plain_message(data, conn->user, conn->passwd, &resp, &len); } } if(!result) { if(resp && sasl->params->maxirlen && strlen(mech) + len > sasl->params->maxirlen) { free(resp); resp = NULL; } if(mech) { result = sasl->params->sendauth(conn, mech, resp); if(!result) { *progress = SASL_INPROGRESS; state(sasl, conn, resp? state2: state1); } } } free(resp); return result; }