Loading lib/smtp.c +238 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ * RFC3207 SMTP over TLS * RFC4954 SMTP Authentication * RFC2195 CRAM-MD5 authentication * RFC2831 DIGEST-MD5 authentication * RFC4616 PLAIN authentication * ***************************************************************************/ Loading Loading @@ -82,6 +83,7 @@ #include "rawstr.h" #include "strtoofft.h" #include "curl_base64.h" #include "curl_rand.h" #include "curl_md5.h" #include "curl_hmac.h" #include "curl_gethostname.h" Loading Loading @@ -279,6 +281,33 @@ static int smtp_endofresp(struct pingpong *pp, int *resp) return result; } #ifndef CURL_DISABLE_CRYPTO_AUTH /* Retrieves the value for a corresponding key from the challenge string * returns TRUE if the key could be found, FALSE if it does not exists */ static bool smtp_digest_get_key_value(const unsigned char *chlg, const char *key, char *value, size_t max_val_len, char end_char) { char *find_pos; size_t i; find_pos = strstr((const char *) chlg, key); if(!find_pos) return FALSE; find_pos += strlen(key); for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) value[i] = *find_pos++; value[i] = '\0'; return TRUE; } #endif /* This is the ONLY way to change SMTP state! */ static void state(struct connectdata *conn, smtpstate newstate) Loading @@ -297,6 +326,8 @@ static void state(struct connectdata *conn, "AUTHLOGIN", "AUTHPASSWD", "AUTHCRAM", "AUTHDIGESTMD5", "AUTHDIGESTMD5_RESP", "AUTHNTLM", "AUTHNTLM_TYPE2MSG", "AUTH", Loading Loading @@ -426,7 +457,12 @@ static CURLcode smtp_authenticate(struct connectdata *conn) /* Check supported authentication mechanisms by decreasing order of security. */ #ifndef CURL_DISABLE_CRYPTO_AUTH if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) { if(smtpc->authmechs & SMTP_AUTH_DIGEST_MD5) { mech = "DIGEST-MD5"; state1 = SMTP_AUTHDIGESTMD5; smtpc->authused = SMTP_AUTH_DIGEST_MD5; } else if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) { mech = "CRAM-MD5"; state1 = SMTP_AUTHCRAMMD5; smtpc->authused = SMTP_AUTH_CRAM_MD5; Loading Loading @@ -816,6 +852,199 @@ static CURLcode smtp_state_authcram_resp(struct connectdata *conn, return result; } /* for AUTH DIGEST-MD5 challenge responses */ static CURLcode smtp_state_authdigest_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { static const char table16[] = "0123456789abcdef"; CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; char *chlg64 = data->state.buffer; unsigned char *chlg; size_t chlglen; size_t len = 0; size_t i; char *rplyb64 = NULL; MD5_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[2 * MD5_DIGEST_LEN + 1]; char HA2_hex[2 * MD5_DIGEST_LEN + 1]; char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; char nonce[64]; char realm[128]; char alg[64]; char nonceCount[] = "00000001"; char cnonce[] = "12345678"; /* will be changed */ char method[] = "AUTHENTICATE"; char qop[] = "auth"; char uri[128] = "smtp/"; char response[512]; (void)instate; /* no use for this yet */ if(smtpcode != 334) { failf(data, "Access denied: %d", smtpcode); return CURLE_LOGIN_DENIED; } /* Get the challenge */ for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++) ; chlg = (unsigned char *) NULL; chlglen = 0; result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; /* Retrieve nonce string from the challenge */ if(!smtp_digest_get_key_value(chlg, "nonce=\"", nonce, sizeof(nonce), '\"')) { Curl_safefree(chlg); return CURLE_LOGIN_DENIED; } /* Retrieve realm string from the challenge */ if(!smtp_digest_get_key_value(chlg, "realm=\"", realm, sizeof(realm), '\"')) { /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ strcpy(realm, ""); } /* Retrieve algorithm string from the challenge */ if(!smtp_digest_get_key_value(chlg, "algorithm=", alg, sizeof(alg), ',')) { Curl_safefree(chlg); return CURLE_LOGIN_DENIED; } Curl_safefree(chlg); /* We do not support other algorithms */ if(strcmp(alg, "md5-sess") != 0) return CURLE_LOGIN_DENIED; /* Generate 64 bits of random data */ for(i = 0; i < 8; i++) cnonce[i] = table16[Curl_rand()%16]; /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) conn->user, strlen(conn->user)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) realm, strlen(realm)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, strlen(conn->passwd)); Curl_MD5_final(ctxt, digest); ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonce, strlen(nonce)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) cnonce, strlen(cnonce)); Curl_MD5_final(ctxt, digest); /* Convert calculated 16 octet hex into 32 bytes string */ for(i = 0; i < MD5_DIGEST_LEN; i++) snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Orepare URL string, append realm to the protocol */ strcat(uri, realm); /* Calculate H(A2) */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) method, strlen(method)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) uri, strlen(uri)); Curl_MD5_final(ctxt, digest); for(i = 0; i < MD5_DIGEST_LEN; i++) snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); /* Now calculate the response hash */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonce, strlen(nonce)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, strlen(nonceCount)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) cnonce, strlen(cnonce)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) qop, strlen(qop)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN); Curl_MD5_final(ctxt, digest); for(i = 0; i < MD5_DIGEST_LEN; i++) snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); strcpy(response, "username=\""); strcat(response, conn->user); strcat(response, "\",realm=\""); strcat(response, realm); strcat(response, "\",nonce=\""); strcat(response, nonce); strcat(response, "\",cnonce=\""); strcat(response, cnonce); strcat(response, "\",nc="); strcat(response, nonceCount); strcat(response, ",digest-uri=\""); strcat(response, uri); strcat(response, "\",response="); strcat(response, resp_hash_hex); /* Encode it to base64 and send it */ result = Curl_base64_encode(data, response, 0, &rplyb64, &len); if(!result) { if(rplyb64) { result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); if(!result) state(conn, SMTP_AUTHDIGESTMD5_RESP); } Curl_safefree(rplyb64); } return result; } /* For AUTH DIGEST-MD5 challenge-response responses */ static CURLcode smtp_state_authdigest_resp_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; (void)instate; /* no use for this yet */ if(smtpcode != 334) { failf(data, "Authentication failed: %d", smtpcode); result = CURLE_LOGIN_DENIED; } else { result = Curl_pp_sendf(&conn->proto.smtpc.pp, ""); if(!result) state(conn, SMTP_AUTH); } return result; } #endif #ifdef USE_NTLM Loading Loading @@ -1170,6 +1399,14 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) case SMTP_AUTHCRAMMD5: result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state); break; case SMTP_AUTHDIGESTMD5: result = smtp_state_authdigest_resp(conn, smtpcode, smtpc->state); break; case SMTP_AUTHDIGESTMD5_RESP: result = smtp_state_authdigest_resp_resp(conn, smtpcode, smtpc->state); break; #endif #ifdef USE_NTLM Loading lib/smtp.h +2 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,8 @@ typedef enum { SMTP_AUTHLOGIN, SMTP_AUTHPASSWD, SMTP_AUTHCRAMMD5, SMTP_AUTHDIGESTMD5, SMTP_AUTHDIGESTMD5_RESP, SMTP_AUTHNTLM, SMTP_AUTHNTLM_TYPE2MSG, SMTP_AUTH, Loading Loading
lib/smtp.c +238 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ * RFC3207 SMTP over TLS * RFC4954 SMTP Authentication * RFC2195 CRAM-MD5 authentication * RFC2831 DIGEST-MD5 authentication * RFC4616 PLAIN authentication * ***************************************************************************/ Loading Loading @@ -82,6 +83,7 @@ #include "rawstr.h" #include "strtoofft.h" #include "curl_base64.h" #include "curl_rand.h" #include "curl_md5.h" #include "curl_hmac.h" #include "curl_gethostname.h" Loading Loading @@ -279,6 +281,33 @@ static int smtp_endofresp(struct pingpong *pp, int *resp) return result; } #ifndef CURL_DISABLE_CRYPTO_AUTH /* Retrieves the value for a corresponding key from the challenge string * returns TRUE if the key could be found, FALSE if it does not exists */ static bool smtp_digest_get_key_value(const unsigned char *chlg, const char *key, char *value, size_t max_val_len, char end_char) { char *find_pos; size_t i; find_pos = strstr((const char *) chlg, key); if(!find_pos) return FALSE; find_pos += strlen(key); for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) value[i] = *find_pos++; value[i] = '\0'; return TRUE; } #endif /* This is the ONLY way to change SMTP state! */ static void state(struct connectdata *conn, smtpstate newstate) Loading @@ -297,6 +326,8 @@ static void state(struct connectdata *conn, "AUTHLOGIN", "AUTHPASSWD", "AUTHCRAM", "AUTHDIGESTMD5", "AUTHDIGESTMD5_RESP", "AUTHNTLM", "AUTHNTLM_TYPE2MSG", "AUTH", Loading Loading @@ -426,7 +457,12 @@ static CURLcode smtp_authenticate(struct connectdata *conn) /* Check supported authentication mechanisms by decreasing order of security. */ #ifndef CURL_DISABLE_CRYPTO_AUTH if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) { if(smtpc->authmechs & SMTP_AUTH_DIGEST_MD5) { mech = "DIGEST-MD5"; state1 = SMTP_AUTHDIGESTMD5; smtpc->authused = SMTP_AUTH_DIGEST_MD5; } else if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) { mech = "CRAM-MD5"; state1 = SMTP_AUTHCRAMMD5; smtpc->authused = SMTP_AUTH_CRAM_MD5; Loading Loading @@ -816,6 +852,199 @@ static CURLcode smtp_state_authcram_resp(struct connectdata *conn, return result; } /* for AUTH DIGEST-MD5 challenge responses */ static CURLcode smtp_state_authdigest_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { static const char table16[] = "0123456789abcdef"; CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; char *chlg64 = data->state.buffer; unsigned char *chlg; size_t chlglen; size_t len = 0; size_t i; char *rplyb64 = NULL; MD5_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[2 * MD5_DIGEST_LEN + 1]; char HA2_hex[2 * MD5_DIGEST_LEN + 1]; char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; char nonce[64]; char realm[128]; char alg[64]; char nonceCount[] = "00000001"; char cnonce[] = "12345678"; /* will be changed */ char method[] = "AUTHENTICATE"; char qop[] = "auth"; char uri[128] = "smtp/"; char response[512]; (void)instate; /* no use for this yet */ if(smtpcode != 334) { failf(data, "Access denied: %d", smtpcode); return CURLE_LOGIN_DENIED; } /* Get the challenge */ for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++) ; chlg = (unsigned char *) NULL; chlglen = 0; result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; /* Retrieve nonce string from the challenge */ if(!smtp_digest_get_key_value(chlg, "nonce=\"", nonce, sizeof(nonce), '\"')) { Curl_safefree(chlg); return CURLE_LOGIN_DENIED; } /* Retrieve realm string from the challenge */ if(!smtp_digest_get_key_value(chlg, "realm=\"", realm, sizeof(realm), '\"')) { /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ strcpy(realm, ""); } /* Retrieve algorithm string from the challenge */ if(!smtp_digest_get_key_value(chlg, "algorithm=", alg, sizeof(alg), ',')) { Curl_safefree(chlg); return CURLE_LOGIN_DENIED; } Curl_safefree(chlg); /* We do not support other algorithms */ if(strcmp(alg, "md5-sess") != 0) return CURLE_LOGIN_DENIED; /* Generate 64 bits of random data */ for(i = 0; i < 8; i++) cnonce[i] = table16[Curl_rand()%16]; /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) conn->user, strlen(conn->user)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) realm, strlen(realm)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, strlen(conn->passwd)); Curl_MD5_final(ctxt, digest); ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonce, strlen(nonce)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) cnonce, strlen(cnonce)); Curl_MD5_final(ctxt, digest); /* Convert calculated 16 octet hex into 32 bytes string */ for(i = 0; i < MD5_DIGEST_LEN; i++) snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Orepare URL string, append realm to the protocol */ strcat(uri, realm); /* Calculate H(A2) */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) method, strlen(method)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) uri, strlen(uri)); Curl_MD5_final(ctxt, digest); for(i = 0; i < MD5_DIGEST_LEN; i++) snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); /* Now calculate the response hash */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonce, strlen(nonce)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, strlen(nonceCount)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) cnonce, strlen(cnonce)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) qop, strlen(qop)); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN); Curl_MD5_final(ctxt, digest); for(i = 0; i < MD5_DIGEST_LEN; i++) snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); strcpy(response, "username=\""); strcat(response, conn->user); strcat(response, "\",realm=\""); strcat(response, realm); strcat(response, "\",nonce=\""); strcat(response, nonce); strcat(response, "\",cnonce=\""); strcat(response, cnonce); strcat(response, "\",nc="); strcat(response, nonceCount); strcat(response, ",digest-uri=\""); strcat(response, uri); strcat(response, "\",response="); strcat(response, resp_hash_hex); /* Encode it to base64 and send it */ result = Curl_base64_encode(data, response, 0, &rplyb64, &len); if(!result) { if(rplyb64) { result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); if(!result) state(conn, SMTP_AUTHDIGESTMD5_RESP); } Curl_safefree(rplyb64); } return result; } /* For AUTH DIGEST-MD5 challenge-response responses */ static CURLcode smtp_state_authdigest_resp_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; (void)instate; /* no use for this yet */ if(smtpcode != 334) { failf(data, "Authentication failed: %d", smtpcode); result = CURLE_LOGIN_DENIED; } else { result = Curl_pp_sendf(&conn->proto.smtpc.pp, ""); if(!result) state(conn, SMTP_AUTH); } return result; } #endif #ifdef USE_NTLM Loading Loading @@ -1170,6 +1399,14 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) case SMTP_AUTHCRAMMD5: result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state); break; case SMTP_AUTHDIGESTMD5: result = smtp_state_authdigest_resp(conn, smtpcode, smtpc->state); break; case SMTP_AUTHDIGESTMD5_RESP: result = smtp_state_authdigest_resp_resp(conn, smtpcode, smtpc->state); break; #endif #ifdef USE_NTLM Loading
lib/smtp.h +2 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,8 @@ typedef enum { SMTP_AUTHLOGIN, SMTP_AUTHPASSWD, SMTP_AUTHCRAMMD5, SMTP_AUTHDIGESTMD5, SMTP_AUTHDIGESTMD5_RESP, SMTP_AUTHNTLM, SMTP_AUTHNTLM_TYPE2MSG, SMTP_AUTH, Loading