static int appenddata(char **dst_buf, /* dest buffer */ size_t *dst_len, /* dest buffer data length */ size_t *dst_alloc, /* dest buffer allocated size */ char *src_buf, /* source buffer */ int src_b64) /* != 0 if source is base64 encoded */ { size_t need_alloc, src_len; union { unsigned char *as_uchar; char *as_char; } buf64; src_len = strlen(src_buf); if(!src_len) return GPE_OK; buf64.as_char = NULL; if(src_b64) { /* base64 decode the given buffer */ src_len = Curl_base64_decode(src_buf, &buf64.as_uchar); src_buf = buf64.as_char; if(!src_len || !src_buf) { /* ** currently there is no way to tell apart an OOM condition in ** Curl_base64_decode() from zero length decoded data. For now, ** let's just assume it is an OOM condition, currently we have ** no input for this function that decodes to zero length data. */ if(buf64.as_char) free(buf64.as_char); return GPE_OUT_OF_MEMORY; } } need_alloc = src_len + *dst_len + 1; /* enlarge destination buffer if required */ if(need_alloc > *dst_alloc) { size_t newsize = need_alloc * 2; char *newptr = realloc(*dst_buf, newsize); if(!newptr) { if(buf64.as_char) free(buf64.as_char); return GPE_OUT_OF_MEMORY; } *dst_alloc = newsize; *dst_buf = newptr; } /* memcpy to support binary blobs */ memcpy(*dst_buf + *dst_len, src_buf, src_len); *dst_len += src_len; *(*dst_buf + *dst_len) = '\0'; if(buf64.as_char) free(buf64.as_char); return GPE_OK; }
/* * Curl_sasl_decode_ntlm_type2_message() * * This is used to decode an already encoded NTLM type-2 message. * * Parameters: * * data [in] - The session handle. * type2msg [in] - The base64 encoded type-2 message. * ntlm [in/out] - The ntlm data struct being used and modified. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_decode_ntlm_type2_message(struct SessionHandle *data, const char *type2msg, struct ntlmdata *ntlm) { CURLcode result = CURLE_OK; unsigned char *type2 = NULL; size_t type2_len = 0; #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) data; #endif /* Decode the base-64 encoded type-2 message */ if(strlen(type2msg) && *type2msg != '=') { result = Curl_base64_decode(type2msg, &type2, &type2_len); if(result) return result; } /* Ensure we have a valid type-2 message */ if(!type2) { infof(data, "NTLM handshake failure (empty type-2 message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Simply store the challenge for use later */ ntlm->input_token = type2; ntlm->input_token_len = type2_len; return result; }
int Curl_sec_read_msg(struct connectdata *conn, char *s, int level) { int len; unsigned char *buf; int code; len = Curl_base64_decode(s + 4, &buf); /* XXX */ if(len > 0) len = (conn->mech->decode)(conn->app_data, buf, len, level, conn); else return -1; if(len < 0) { free(buf); return -1; } buf[len] = '\0'; if(buf[3] == '-') code = 0; else sscanf((char *)buf, "%d", &code); if(buf[len-1] == '\n') buf[len-1] = '\0'; strcpy(s, (char *)buf); free(buf); return code; }
int Curl_sec_read_msg(struct connectdata *conn, char *s, int level) { int len; char *buf; int code; buf = malloc(strlen(s)); len = Curl_base64_decode(s + 4, buf); /* XXX */ len = (conn->mech->decode)(conn->app_data, buf, len, level, conn); if(len < 0) return -1; buf[len] = '\0'; if(buf[3] == '-') code = 0; else sscanf(buf, "%d", &code); if(buf[len-1] == '\n') buf[len-1] = '\0'; strcpy(s, buf); free(buf); return code; }
int Curl_sec_read_msg(struct connectdata *conn, char *buffer, enum protection_level level) { /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an int */ int decoded_len; char *buf; int ret_code = 0; size_t decoded_sz = 0; CURLcode error; if(!conn->mech) /* not inititalized, return error */ return -1; DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); if(error || decoded_sz == 0) return -1; if(decoded_sz > (size_t)INT_MAX) { free(buf); return -1; } decoded_len = curlx_uztosi(decoded_sz); decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, level, conn); if(decoded_len <= 0) { free(buf); return -1; } if(conn->data->set.verbose) { buf[decoded_len] = '\n'; Curl_debug(conn->data, CURLINFO_HEADER_IN, buf, decoded_len + 1, conn); } buf[decoded_len] = '\0'; if(decoded_len <= 3) /* suspiciously short */ return 0; if(buf[3] != '-') /* safe to ignore return code */ (void)sscanf(buf, "%d", &ret_code); if(buf[decoded_len - 1] == '\n') buf[decoded_len - 1] = '\0'; /* FIXME: Is |buffer| length always greater than |decoded_len|? */ strcpy(buffer, buf); free(buf); return ret_code; }
static CURLcode pubkey_pem_to_der(const char *pem, unsigned char **der, size_t *der_len) { char *stripped_pem, *begin_pos, *end_pos; size_t pem_count, stripped_pem_count = 0, pem_len; CURLcode result; /* if no pem, exit. */ if(!pem) return CURLE_BAD_CONTENT_ENCODING; begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----"); if(!begin_pos) return CURLE_BAD_CONTENT_ENCODING; pem_count = begin_pos - pem; /* Invalid if not at beginning AND not directly following \n */ if(0 != pem_count && '\n' != pem[pem_count - 1]) return CURLE_BAD_CONTENT_ENCODING; /* 26 is length of "-----BEGIN PUBLIC KEY-----" */ pem_count += 26; /* Invalid if not directly following \n */ end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----"); if(!end_pos) return CURLE_BAD_CONTENT_ENCODING; pem_len = end_pos - pem; stripped_pem = malloc(pem_len - pem_count + 1); if(!stripped_pem) return CURLE_OUT_OF_MEMORY; /* * Here we loop through the pem array one character at a time between the * correct indices, and place each character that is not '\n' or '\r' * into the stripped_pem array, which should represent the raw base64 string */ while(pem_count < pem_len) { if('\n' != pem[pem_count] && '\r' != pem[pem_count]) stripped_pem[stripped_pem_count++] = pem[pem_count]; ++pem_count; } /* Place the null terminator in the correct place */ stripped_pem[stripped_pem_count] = '\0'; result = Curl_base64_decode(stripped_pem, der, der_len); Curl_safefree(stripped_pem); return result; }
/* * auth_decode_digest_md5_message() * * This is used internally to decode an already encoded DIGEST-MD5 challenge * message into the seperate attributes. * * Parameters: * * chlg64 [in] - The base64 encoded challenge message. * nonce [in/out] - The buffer where the nonce will be stored. * nlen [in] - The length of the nonce buffer. * realm [in/out] - The buffer where the realm will be stored. * rlen [in] - The length of the realm buffer. * alg [in/out] - The buffer where the algorithm will be stored. * alen [in] - The length of the algorithm buffer. * qop [in/out] - The buffer where the qop-options will be stored. * qlen [in] - The length of the qop buffer. * * Returns CURLE_OK on success. */ static CURLcode auth_decode_digest_md5_message(const char *chlg64, char *nonce, size_t nlen, char *realm, size_t rlen, char *alg, size_t alen, char *qop, size_t qlen) { CURLcode result = CURLE_OK; unsigned char *chlg = NULL; size_t chlglen = 0; size_t chlg64len = strlen(chlg64); /* Decode the base-64 encoded challenge message */ if(chlg64len && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) return CURLE_BAD_CONTENT_ENCODING; /* Retrieve nonce string from the challenge */ if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen, '\"')) { free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Retrieve realm string from the challenge */ if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen, '\"')) { /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ strcpy(realm, ""); } /* Retrieve algorithm string from the challenge */ if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) { free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Retrieve qop-options string from the challenge */ if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) { free(chlg); return CURLE_BAD_CONTENT_ENCODING; } free(chlg); return CURLE_OK; }
/* * Curl_sasl_create_cram_md5_message() * * This is used to generate an already encoded CRAM-MD5 response message ready * for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - Pointer to the base64 encoded challenge buffer. * userp [in] - The user name. * passdwp [in] - The user's password. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, const char* chlg64, const char* userp, const char* passwdp, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlg64len = strlen(chlg64); unsigned char *chlg = (unsigned char *) NULL; size_t chlglen = 0; HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char response[MAX_CURL_USER_LENGTH + 2 * MD5_DIGEST_LEN + 1]; /* Decode the challenge if necessary */ if(chlg64len && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Compute the digest using the password as the key */ ctxt = Curl_HMAC_init(Curl_HMAC_MD5, (const unsigned char *) passwdp, curlx_uztoui(strlen(passwdp))); if(!ctxt) { Curl_safefree(chlg); return CURLE_OUT_OF_MEMORY; } /* Update the digest with the given challenge */ if(chlglen > 0) Curl_HMAC_update(ctxt, chlg, curlx_uztoui(chlglen)); Curl_safefree(chlg); /* Finalise the digest */ Curl_HMAC_final(ctxt, digest); /* Prepare the response */ snprintf(response, sizeof(response), "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", userp, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); /* Base64 encode the reply */ return Curl_base64_encode(data, response, 0, outptr, outlen); }
static char *appendstring(char *string, /* original string */ char *buffer, /* to append */ size_t *stringlen, /* length of string */ size_t *stralloc, /* allocated size */ char base64) /* 1 if base64 encoded */ { union { unsigned char * as_uchar; char * as_char; } buf64; size_t len = strlen(buffer); size_t needed_len = len + *stringlen + 1; buf64.as_char = NULL; if(base64) { /* decode the given buffer first */ len = Curl_base64_decode(buffer, &buf64.as_uchar); /* updated len */ buffer = buf64.as_char; needed_len = len + *stringlen + 1; /* recalculate */ } if(needed_len >= *stralloc) { char *newptr; size_t newsize = needed_len*2; /* get twice the needed size */ newptr = realloc(string, newsize); if(newptr) { string = newptr; *stralloc = newsize; } else { if(buf64.as_char) free(buf64.as_char); return NULL; } } /* memcpy to support binary blobs */ memcpy(&string[*stringlen], buffer, len); *stringlen += len; string[*stringlen]=0; if(buf64.as_char) free(buf64.as_char); return string; }
/* * sasl_decode_cram_md5_message() * * This is used to decode an already encoded CRAM-MD5 challenge message. * * Parameters: * * chlg64 [in] - The base64 encoded challenge message. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode sasl_decode_cram_md5_message(const char *chlg64, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlg64len = strlen(chlg64); *outptr = NULL; *outlen = 0; /* Decode the challenge if necessary */ if(chlg64len && *chlg64 != '=') result = Curl_base64_decode(chlg64, (unsigned char **) outptr, outlen); return result; }
static int decodedata(char **buf, /* dest buffer */ size_t *len) /* dest buffer data length */ { int error = 0; unsigned char *buf64 = NULL; size_t src_len = 0; if(!*len) return GPE_OK; /* base64 decode the given buffer */ error = (int) Curl_base64_decode(*buf, &buf64, &src_len); if(error) return GPE_OUT_OF_MEMORY; if(!src_len) { /* ** currently there is no way to tell apart an OOM condition in ** Curl_base64_decode() from zero length decoded data. For now, ** let's just assume it is an OOM condition, currently we have ** no input for this function that decodes to zero length data. */ free(buf64); return GPE_OUT_OF_MEMORY; } /* memcpy to support binary blobs */ memcpy(*buf, buf64, src_len); *len = src_len; *(*buf + src_len) = '\0'; free(buf64); return GPE_OK; }
/* returning zero (0) means success, everything else is treated as "failure" with no care exactly what the failure was */ int Curl_input_negotiate(struct connectdata *conn, bool proxy, const char *header) { struct SessionHandle *data = conn->data; struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: &data->state.negotiate; OM_uint32 major_status, minor_status, discard_st, min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret; size_t len; size_t rawlen = 0; bool gss; const char* protocol; CURLcode error; while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; gss = TRUE; } else if(checkprefix("Negotiate", header)) { protocol = "Negotiate"; gss = FALSE; } else return -1; if(neg_ctx->context) { if(neg_ctx->gss != gss) { return -1; } } else { neg_ctx->protocol = protocol; neg_ctx->gss = gss; } if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(data); return -1; } if(neg_ctx->server_name == NULL && (ret = get_gss_name(conn, proxy, &neg_ctx->server_name))) return ret; header += strlen(neg_ctx->protocol); while(*header && ISSPACE(*header)) header++; len = strlen(header); if(len > 0) { error = Curl_base64_decode(header, (unsigned char **)&input_token.value, &rawlen); if(error || rawlen == 0) return -1; input_token.length = rawlen; DEBUGASSERT(input_token.value != NULL); #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if(checkprefix("Negotiate", header)) { unsigned char *spnegoToken = NULL; size_t spnegoTokenLength = 0; gss_buffer_desc mechToken = GSS_C_EMPTY_BUFFER; spnegoToken = malloc(input_token.length); if(spnegoToken == NULL) { Curl_safefree(input_token.value); return CURLE_OUT_OF_MEMORY; } memcpy(spnegoToken, input_token.value, input_token.length); spnegoTokenLength = input_token.length; if(!parseSpnegoTargetToken(spnegoToken, spnegoTokenLength, NULL, NULL, (unsigned char**)&mechToken.value, &mechToken.length, NULL, NULL)) { Curl_safefree(spnegoToken); infof(data, "Parse SPNEGO Target Token failed\n"); } else if(!mechToken.value || !mechToken.length) { Curl_safefree(spnegoToken); if(mechToken.value) gss_release_buffer(&discard_st, &mechToken); infof(data, "Parse SPNEGO Target Token succeeded (NULL token)\n"); } else { Curl_safefree(spnegoToken); Curl_safefree(input_token.value); input_token.value = malloc(mechToken.length); if(input_token.value == NULL) { gss_release_buffer(&discard_st, &mechToken); return CURLE_OUT_OF_MEMORY; } memcpy(input_token.value, mechToken.value, mechToken.length); input_token.length = mechToken.length; gss_release_buffer(&discard_st, &mechToken); infof(data, "Parse SPNEGO Target Token succeeded\n"); } } #endif } major_status = Curl_gss_init_sec_context(conn, &minor_status, &neg_ctx->context, neg_ctx->server_name, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, NULL); gss_release_cred(&min_stat, &conn->data->curl_gss_creds); Curl_safefree(input_token.value); /*To remove the memory leak issue*/ if(neg_ctx->server_name != GSS_C_NO_NAME) gss_release_name(&min_stat, &neg_ctx->server_name); neg_ctx->status = major_status; if(GSS_ERROR(major_status)) { if(output_token.value) gss_release_buffer(&discard_st, &output_token); log_gss_error(conn, minor_status, "KRB5_ERROR: gss_init_sec_context() failed: "); printf("\n KRB5_ERROR: gss_init_sec_context() failed error code : %d", minor_status); return -1; } if(!output_token.value || !output_token.length) { if(output_token.value) gss_release_buffer(&discard_st, &output_token); return -1; } neg_ctx->output_token = output_token; return 0; }
/* for AUTH CRAM-MD5 responses. */ static CURLcode smtp_state_authcram_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; char * chlg64 = data->state.buffer; unsigned char * chlg; size_t chlglen; size_t l; char * rplyb64; HMAC_context * ctxt; unsigned char digest[16]; char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1]; (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; if(*chlg64 != '=') { for (l = strlen(chlg64); l--;) if(chlg64[l] != '\r' && chlg64[l] != '\n' && chlg64[l] != ' ' && chlg64[l] != '\t') break; if(++l) { chlg64[l] = '\0'; if(!(chlglen = Curl_base64_decode(chlg64, &chlg))) return CURLE_OUT_OF_MEMORY; } } /* Compute digest. */ ctxt = Curl_HMAC_init(Curl_HMAC_MD5, (const unsigned char *) conn->passwd, (unsigned int)(strlen(conn->passwd))); if(!ctxt) { if(chlg) free(chlg); return CURLE_OUT_OF_MEMORY; } if(chlglen > 0) Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen)); if(chlg) free(chlg); Curl_HMAC_final(ctxt, digest); /* Prepare the reply. */ snprintf(reply, sizeof reply, "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", conn->user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); /* Encode it to base64 and send it. */ l = Curl_base64_encode(data, reply, 0, &rplyb64); if(!l) result = CURLE_OUT_OF_MEMORY; else { result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); free(rplyb64); if(!result) state(conn, SMTP_AUTH); } return result; }
int Curl_input_negotiate(struct connectdata *conn, char *header) { struct negotiatedata *neg_ctx = &conn->data->state.negotiate; OM_uint32 major_status, minor_status, minor_status2; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret; size_t len; bool gss; const char* protocol; while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; gss = TRUE; } else if (checkprefix("Negotiate", header)) { protocol = "Negotiate"; gss = FALSE; } else return -1; if (neg_ctx->context) { if (neg_ctx->gss != gss) { return -1; } } else { neg_ctx->protocol = protocol; neg_ctx->gss = gss; } if (neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished succesfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(conn->data); return -1; } if (neg_ctx->server_name == NULL && (ret = get_gss_name(conn, &neg_ctx->server_name))) return ret; header += strlen(neg_ctx->protocol); while(*header && ISSPACE(*header)) header++; len = strlen(header); if (len > 0) { int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value); if (rawlen < 0) return -1; input_token.length = rawlen; #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if (checkprefix("Negotiate", header)) { ASN1_OBJECT * object = NULL; int rc = 1; unsigned char * spnegoToken = NULL; size_t spnegoTokenLength = 0; unsigned char * mechToken = NULL; size_t mechTokenLength = 0; spnegoToken = malloc(input_token.length); if (input_token.value == NULL) return ENOMEM; spnegoTokenLength = input_token.length; object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); if (!parseSpnegoTargetToken(spnegoToken, spnegoTokenLength, NULL, NULL, &mechToken, &mechTokenLength, NULL, NULL)) { free(spnegoToken); spnegoToken = NULL; infof(conn->data, "Parse SPNEGO Target Token failed\n"); } else { free(input_token.value); input_token.value = NULL; input_token.value = malloc(mechTokenLength); memcpy(input_token.value, mechToken,mechTokenLength); input_token.length = mechTokenLength; free(mechToken); mechToken = NULL; infof(conn->data, "Parse SPNEGO Target Token succeeded\n"); } } #endif } major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &neg_ctx->context, neg_ctx->server_name, GSS_C_NO_OID, GSS_C_DELEG_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if (input_token.length > 0) gss_release_buffer(&minor_status2, &input_token); neg_ctx->status = major_status; if (GSS_ERROR(major_status)) { /* Curl_cleanup_negotiate(conn->data) ??? */ log_gss_error(conn, minor_status, (char *)"gss_init_sec_context() failed: "); return -1; } if (output_token.length == 0) { return -1; } neg_ctx->output_token = output_token; /* conn->bits.close = FALSE; */ return 0; }
/* * Curl_auth_decode_spnego_message() * * This is used to decode an already encoded SPNEGO (Negotiate) challenge * message. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * host [in] - The host name. * chlg64 [in] - The optional base64 encoded challenge message. * nego [in/out] - The Negotiate data struct being used and modified. * * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct SessionHandle *data, const char *user, const char *password, const char *service, const char *host, const char *chlg64, struct negotiatedata *nego) { CURLcode result = CURLE_OK; size_t chlglen = 0; unsigned char *chlg = NULL; OM_uint32 major_status; OM_uint32 minor_status; OM_uint32 unused_status; gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; (void) user; (void) password; if(nego->context && nego->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_auth_spnego_cleanup(nego); return CURLE_LOGIN_DENIED; } if(!nego->spn) { /* Generate our SPN */ char *spn = Curl_auth_build_spn(service, NULL, host); if(!spn) return CURLE_OUT_OF_MEMORY; /* Populate the SPN structure */ spn_token.value = spn; spn_token.length = strlen(spn); /* Import the SPN */ major_status = gss_import_name(&minor_status, &spn_token, GSS_C_NT_HOSTBASED_SERVICE, &nego->spn); if(GSS_ERROR(major_status)) { Curl_gss_log_error(data, "gss_import_name() failed: ", major_status, minor_status); free(spn); return CURLE_OUT_OF_MEMORY; } free(spn); } if(chlg64 && *chlg64) { /* Decode the base-64 encoded challenge message */ if(*chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "SPNEGO handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Setup the challenge "input" security buffer */ input_token.value = chlg; input_token.length = chlglen; } /* Generate our challenge-response message */ major_status = Curl_gss_init_sec_context(data, &minor_status, &nego->context, nego->spn, &Curl_spnego_mech_oid, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, TRUE, NULL); /* Free the decoded challenge as it is not required anymore */ Curl_safefree(input_token.value); nego->status = major_status; if(GSS_ERROR(major_status)) { if(output_token.value) gss_release_buffer(&unused_status, &output_token); Curl_gss_log_error(data, "gss_init_sec_context() failed: ", major_status, minor_status); return CURLE_OUT_OF_MEMORY; } if(!output_token.value || !output_token.length) { if(output_token.value) gss_release_buffer(&unused_status, &output_token); return CURLE_OUT_OF_MEMORY; } nego->output_token = output_token; return CURLE_OK; }
static int krb4_auth(void *app_data, struct connectdata *conn) { int ret; char *p; unsigned char *ptr; size_t len; KTEXT_ST adat; MSG_DAT msg_data; int checksum; u_int32_t cs; struct krb4_data *d = app_data; char *host = conn->host.name; ssize_t nread; int l = sizeof(conn->local_addr); struct SessionHandle *data = conn->data; CURLcode result; if(getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)LOCAL_ADDR, &l) < 0) perror("getsockname()"); checksum = getpid(); ret = mk_auth(d, &adat, "ftp", host, checksum); if(ret == KDC_PR_UNKNOWN) ret = mk_auth(d, &adat, "rcmd", host, checksum); if(ret) { infof(data, "%s\n", krb_get_err_text(ret)); return AUTH_CONTINUE; } #ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM if(krb_get_config_bool("nat_in_use")) { struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; struct in_addr natAddr; if(krb_get_our_ip_for_realm(krb_realmofhost(host), &natAddr) != KSUCCESS && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS) infof(data, "Can't get address for realm %s\n", krb_realmofhost(host)); else { if(natAddr.s_addr != localaddr->sin_addr.s_addr) { char addr_buf[128]; if(Curl_inet_ntop(AF_INET, natAddr, addr_buf, sizeof(addr_buf))) infof(data, "Using NAT IP address (%s) for kerberos 4\n", addr_buf); localaddr->sin_addr = natAddr; } } } #endif if(Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, &p) < 1) { Curl_failf(data, "Out of memory base64-encoding"); return AUTH_CONTINUE; } result = Curl_ftpsendf(conn, "ADAT %s", p); free(p); if(result) return -2; if(Curl_GetFTPResponse(&nread, conn, NULL)) return -1; if(data->state.buffer[0] != '2'){ Curl_failf(data, "Server didn't accept auth data"); return AUTH_ERROR; } p = strstr(data->state.buffer, "ADAT="); if(!p) { Curl_failf(data, "Remote host didn't send adat reply"); return AUTH_ERROR; } p += 5; len = Curl_base64_decode(p, &ptr); if(len > sizeof(adat.dat)-1) { free(ptr); len=0; } if(!len || !ptr) { Curl_failf(data, "Failed to decode base64 from server"); return AUTH_ERROR; } memcpy((char *)adat.dat, ptr, len); free(ptr); adat.length = len; ret = krb_rd_safe(adat.dat, adat.length, &d->key, (struct sockaddr_in *)hisctladdr, (struct sockaddr_in *)myctladdr, &msg_data); if(ret) { Curl_failf(data, "Error reading reply from server: %s", krb_get_err_text(ret)); return AUTH_ERROR; } krb_get_int(msg_data.app_data, &cs, 4, 0); if(cs - checksum != 1) { Curl_failf(data, "Bad checksum returned from server"); return AUTH_ERROR; } return AUTH_OK; }
static int krb5_auth(void *app_data, struct connectdata *conn) { int ret; char *p; const char *host = conn->dns_entry->addr->ai_canonname; ssize_t nread; curl_socklen_t l = sizeof(conn->local_addr); struct SessionHandle *data = conn->data; CURLcode result; const char *service = "ftp", *srv_host = "host"; gss_buffer_desc gssbuf, _gssresp, *gssresp; OM_uint32 maj, min; gss_name_t gssname; gss_ctx_id_t *context = app_data; struct gss_channel_bindings_struct chan; if(getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)LOCAL_ADDR, &l) < 0) perror("getsockname()"); chan.initiator_addrtype = GSS_C_AF_INET; chan.initiator_address.length = l - 4; chan.initiator_address.value = &((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr; chan.acceptor_addrtype = GSS_C_AF_INET; chan.acceptor_address.length = l - 4; chan.acceptor_address.value = &((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr; chan.application_data.length = 0; chan.application_data.value = NULL; /* this loop will execute twice (once for service, once for host) */ while(1) { /* this really shouldn't be repeated here, but can't help it */ if(service == srv_host) { result = Curl_ftpsendf(conn, "AUTH GSSAPI"); if(result) return -2; if(Curl_GetFTPResponse(&nread, conn, NULL)) return -1; if(data->state.buffer[0] != '3') return -1; } gssbuf.value = data->state.buffer; gssbuf.length = snprintf(gssbuf.value, BUFSIZE, "%s@%s", service, host); maj = gss_import_name(&min, &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &gssname); if(maj != GSS_S_COMPLETE) { gss_release_name(&min, &gssname); if(service == srv_host) { Curl_failf(data, "Error importing service name %s", gssbuf.value); return AUTH_ERROR; } service = srv_host; continue; } { gss_OID t; gss_display_name(&min, gssname, &gssbuf, &t); Curl_infof(data, "Trying against %s\n", gssbuf.value); gss_release_buffer(&min, &gssbuf); } gssresp = GSS_C_NO_BUFFER; *context = GSS_C_NO_CONTEXT; do { ret = AUTH_OK; maj = gss_init_sec_context(&min, GSS_C_NO_CREDENTIAL, context, gssname, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, &chan, gssresp, NULL, &gssbuf, NULL, NULL); if(gssresp) { free(_gssresp.value); gssresp = NULL; } if(maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { Curl_infof(data, "Error creating security context"); ret = AUTH_ERROR; break; } if(gssbuf.length != 0) { if(Curl_base64_encode(data, (char *)gssbuf.value, gssbuf.length, &p) < 1) { Curl_infof(data, "Out of memory base64-encoding"); ret = AUTH_CONTINUE; break; } result = Curl_ftpsendf(conn, "ADAT %s", p); free(p); if(result) { ret = -2; break; } if(Curl_GetFTPResponse(&nread, conn, NULL)) { ret = -1; break; } if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3'){ Curl_infof(data, "Server didn't accept auth data\n"); ret = AUTH_ERROR; break; } p = data->state.buffer + 4; p = strstr(p, "ADAT="); if(p) { _gssresp.length = Curl_base64_decode(p + 5, (unsigned char **) &_gssresp.value); if(_gssresp.length < 1) { Curl_failf(data, "Out of memory base64-encoding"); ret = AUTH_CONTINUE; break; } } gssresp = &_gssresp; } } while(maj == GSS_S_CONTINUE_NEEDED); gss_release_name(&min, &gssname); if(gssresp) free(_gssresp.value); if(ret == AUTH_OK || service == srv_host) return ret; service = srv_host; } }
fail_unless( rc == 4 , "return code should be 4" ); verify_memory( output, "aWlp", 4); Curl_safefree(output); rc = Curl_base64_encode(data, "iiii", 4, &output); fail_unless( rc == 8 , "return code should be 8" ); verify_memory( output, "aWlpaQ==", 8); Curl_safefree(output); /* 0 length makes it do strlen() */ rc = Curl_base64_encode(data, "iiii", 0, &output); fail_unless( rc == 8 , "return code should be 8" ); verify_memory( output, "aWlpaQ==", 8); Curl_safefree(output); rc = Curl_base64_decode("aWlpaQ==", &decoded); fail_unless(rc == 4, "return code should be 4"); verify_memory(decoded, "iiii", 4); Curl_safefree(decoded); rc = Curl_base64_decode("aWlp", &decoded); fail_unless(rc == 3, "return code should be 3"); verify_memory(decoded, "iii", 3); Curl_safefree(decoded); rc = Curl_base64_decode("aWk=", &decoded); fail_unless(rc == 2, "return code should be 2"); verify_memory(decoded, "ii", 2); Curl_safefree(decoded); rc = Curl_base64_decode("aQ==", &decoded);
/* * Curl_sasl_create_gssapi_user_message() * * This is used to generate an already encoded GSSAPI (Kerberos V5) user token * message ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * mutual_auth [in] - Flag specifing whether or not mutual authentication * is enabled. * chlg64 [in] - Pointer to the optional base64 encoded challenge * message. * krb5 [in/out] - The gssapi data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *service, const bool mutual_auth, const char *chlg64, struct kerberos5data *krb5, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlglen = 0; unsigned char *chlg = NULL; OM_uint32 gss_status; OM_uint32 gss_major_status; OM_uint32 gss_minor_status; gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; (void) userp; (void) passwdp; if(krb5->context == GSS_C_NO_CONTEXT) { /* Generate our SPN */ char *spn = Curl_sasl_build_gssapi_spn(service, data->easy_conn->host.name); if(!spn) return CURLE_OUT_OF_MEMORY; /* Populate the SPN structure */ spn_token.value = spn; spn_token.length = strlen(spn); /* Import the SPN */ gss_major_status = gss_import_name(&gss_minor_status, &spn_token, gss_nt_service_name, &krb5->spn); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, gss_minor_status, "gss_import_name() failed: "); return CURLE_OUT_OF_MEMORY; } } else { /* Decode the base-64 encoded challenge message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "GSSAPI handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Setup the challenge "input" security buffer */ input_token.value = chlg; input_token.length = chlglen; } gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, &krb5->context, krb5->spn, &Curl_krb5_mech_oid, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, mutual_auth, NULL); Curl_safefree(input_token.value); if(GSS_ERROR(gss_major_status)) { if(output_token.value) gss_release_buffer(&gss_status, &output_token); Curl_gss_log_error(data, gss_minor_status, "gss_init_sec_context() failed: "); return CURLE_RECV_ERROR; } if(output_token.value && output_token.length) { /* Base64 encode the response */ result = Curl_base64_encode(data, (char *) output_token.value, output_token.length, outptr, outlen); gss_release_buffer(&gss_status, &output_token); } return result; }
CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy, /* if proxy or not */ const char *header) /* rest of the www-authenticate: header */ { /* point to the correct struct with this */ struct ntlmdata *ntlm; #ifndef USE_WINDOWS_SSPI static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; #endif ntlm = proxy?&conn->proxyntlm:&conn->ntlm; /* skip initial whitespaces */ while(*header && ISSPACE(*header)) header++; if(checkprefix("NTLM", header)) { header += strlen("NTLM"); while(*header && ISSPACE(*header)) header++; if(*header) { /* We got a type-2 message here: Index Description Content 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) 8 NTLM Message Type long (0x02000000) 12 Target Name security buffer(*) 20 Flags long 24 Challenge 8 bytes (32) Context (optional) 8 bytes (two consecutive longs) (40) Target Information (optional) security buffer(*) 32 (48) start of data block */ size_t size; unsigned char *buffer; size = Curl_base64_decode(header, &buffer); if(!buffer) return CURLNTLM_BAD; ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */ #ifdef USE_WINDOWS_SSPI ntlm->type_2 = malloc(size+1); if(ntlm->type_2 == NULL) { free(buffer); return CURLE_OUT_OF_MEMORY; } ntlm->n_type_2 = size; memcpy(ntlm->type_2, buffer, size); #else ntlm->flags = 0; if((size < 32) || (memcmp(buffer, NTLMSSP_SIGNATURE, 8) != 0) || (memcmp(buffer+8, type2_marker, sizeof(type2_marker)) != 0)) { /* This was not a good enough type-2 message */ free(buffer); return CURLNTLM_BAD; } ntlm->flags = readint_le(&buffer[20]); memcpy(ntlm->nonce, &buffer[24], 8); DEBUG_OUT({ fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); print_flags(stderr, ntlm->flags); fprintf(stderr, "\n nonce="); print_hex(stderr, (char *)ntlm->nonce, 8); fprintf(stderr, "\n****\n"); fprintf(stderr, "**** Header %s\n ", header); }); #endif free(buffer); }
/* * Curl_auth_decode_ntlm_type2_message() * * This is used to decode an already encoded NTLM type-2 message. The message * is first decoded from a base64 string into a raw NTLM message and checked * for validity before the appropriate data for creating a type-3 message is * written to the given NTLM data structure. * * Parameters: * * data [in] - The session handle. * type2msg [in] - The base64 encoded type-2 message. * ntlm [in/out] - The NTLM data struct being used and modified. * * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, const char *type2msg, struct ntlmdata *ntlm) { static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; /* NTLM type-2 message structure: Index Description Content 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) 8 NTLM Message Type long (0x02000000) 12 Target Name security buffer 20 Flags long 24 Challenge 8 bytes (32) Context 8 bytes (two consecutive longs) (*) (40) Target Information security buffer (*) (48) OS Version Structure 8 bytes (*) 32 (48) (56) Start of data block (*) (*) -> Optional */ CURLcode result = CURLE_OK; unsigned char *type2 = NULL; size_t type2_len = 0; #if defined(NTLM_NEEDS_NSS_INIT) /* Make sure the crypto backend is initialized */ result = Curl_nss_force_init(data); if(result) return result; #elif defined(CURL_DISABLE_VERBOSE_STRINGS) (void)data; #endif /* Decode the base-64 encoded type-2 message */ if(strlen(type2msg) && *type2msg != '=') { result = Curl_base64_decode(type2msg, &type2, &type2_len); if(result) return result; } /* Ensure we have a valid type-2 message */ if(!type2) { infof(data, "NTLM handshake failure (empty type-2 message)\n"); return CURLE_BAD_CONTENT_ENCODING; } ntlm->flags = 0; if((type2_len < 32) || (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { /* This was not a good enough type-2 message */ free(type2); infof(data, "NTLM handshake failure (bad type-2 message)\n"); return CURLE_BAD_CONTENT_ENCODING; } ntlm->flags = Curl_read32_le(&type2[20]); memcpy(ntlm->nonce, &type2[24], 8); if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) { result = ntlm_decode_type2_target(data, type2, type2_len, ntlm); if(result) { free(type2); infof(data, "NTLM handshake failure (bad type-2 message)\n"); return result; } } DEBUG_OUT({ fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); ntlm_print_flags(stderr, ntlm->flags); fprintf(stderr, "\n nonce="); ntlm_print_hex(stderr, (char *)ntlm->nonce, 8); fprintf(stderr, "\n****\n"); fprintf(stderr, "**** Header %s\n ", header); });
/* * Curl_sasl_create_digest_md5_message() * * This is used to generate an already encoded DIGEST-MD5 response message * ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - Pointer to the base64 encoded challenge buffer. * userp [in] - The user name. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp or pop * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, const char* chlg64, const char* userp, const char* passwdp, const char* service, char **outptr, size_t *outlen) { static const char table16[] = "0123456789abcdef"; CURLcode result = CURLE_OK; unsigned char *chlg = (unsigned char *) NULL; size_t chlglen = 0; size_t i; 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]; char response[512]; result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; /* Retrieve nonce string from the challenge */ if(!sasl_digest_get_key_value(chlg, "nonce=\"", nonce, sizeof(nonce), '\"')) { Curl_safefree(chlg); return CURLE_LOGIN_DENIED; } /* Retrieve realm string from the challenge */ if(!sasl_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(!sasl_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); if(!ctxt) return CURLE_OUT_OF_MEMORY; Curl_MD5_update(ctxt, (const unsigned char *) userp, curlx_uztoui(strlen(userp))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) realm, curlx_uztoui(strlen(realm))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) passwdp, curlx_uztoui(strlen(passwdp))); Curl_MD5_final(ctxt, digest); ctxt = Curl_MD5_init(Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; 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, curlx_uztoui(strlen(nonce))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) cnonce, curlx_uztoui(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]); /* Prepare the URL string */ snprintf(uri, sizeof(uri), "%s/%s", service, realm); /* Calculate H(A2) */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; Curl_MD5_update(ctxt, (const unsigned char *) method, curlx_uztoui(strlen(method))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) uri, curlx_uztoui(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); if(!ctxt) return CURLE_OUT_OF_MEMORY; 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, curlx_uztoui(strlen(nonce))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, curlx_uztoui(strlen(nonceCount))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) cnonce, curlx_uztoui(strlen(cnonce))); Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); Curl_MD5_update(ctxt, (const unsigned char *) qop, curlx_uztoui(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]); snprintf(response, sizeof(response), "username=\"%s\",realm=\"%s\",nonce=\"%s\"," "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s", userp, realm, nonce, cnonce, nonceCount, uri, resp_hash_hex); /* Base64 encode the reply */ return Curl_base64_encode(data, response, 0, outptr, outlen); }
/* * Curl_auth_decode_spnego_message() * * This is used to decode an already encoded SPNEGO (Negotiate) challenge * message. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in] - The host name. * chlg64 [in] - The optional base64 encoded challenge message. * nego [in/out] - The Negotiate data struct being used and modified. * * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, const char *user, const char *password, const char *service, const char *host, const char *chlg64, struct negotiatedata *nego) { CURLcode result = CURLE_OK; size_t chlglen = 0; unsigned char *chlg = NULL; PSecPkgInfo SecurityPackage; SecBuffer chlg_buf; SecBuffer resp_buf; SecBufferDesc chlg_desc; SecBufferDesc resp_desc; unsigned long attrs; TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) data; #endif if(nego->context && nego->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_auth_spnego_cleanup(nego); return CURLE_LOGIN_DENIED; } if(!nego->spn) { /* Generate our SPN */ nego->spn = Curl_auth_build_spn(service, host, NULL); if(!nego->spn) return CURLE_OUT_OF_MEMORY; } if(!nego->output_token) { /* Query the security package for Negotiate */ nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NEGOTIATE), &SecurityPackage); if(nego->status != SEC_E_OK) return CURLE_NOT_BUILT_IN; nego->token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ s_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our output buffer */ nego->output_token = malloc(nego->token_max); if(!nego->output_token) return CURLE_OUT_OF_MEMORY; } if(!nego->credentials) { /* Do we have credientials to use or are we using single sign-on? */ if(user && *user) { /* Populate our identity structure */ result = Curl_create_sspi_identity(user, password, &nego->identity); if(result) return result; /* Allow proper cleanup of the identity structure */ nego->p_identity = &nego->identity; } else /* Use the current Windows user */ nego->p_identity = NULL; /* Allocate our credentials handle */ nego->credentials = malloc(sizeof(CredHandle)); if(!nego->credentials) return CURLE_OUT_OF_MEMORY; memset(nego->credentials, 0, sizeof(CredHandle)); /* Acquire our credentials handle */ nego->status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)TEXT(SP_NAME_NEGOTIATE), SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, nego->credentials, &expiry); if(nego->status != SEC_E_OK) return CURLE_LOGIN_DENIED; /* Allocate our new context handle */ nego->context = malloc(sizeof(CtxtHandle)); if(!nego->context) return CURLE_OUT_OF_MEMORY; memset(nego->context, 0, sizeof(CtxtHandle)); } if(chlg64 && *chlg64) { /* Decode the base-64 encoded challenge message */ if(*chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "SPNEGO handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Setup the challenge "input" security buffer */ chlg_desc.ulVersion = SECBUFFER_VERSION; chlg_desc.cBuffers = 1; chlg_desc.pBuffers = &chlg_buf; chlg_buf.BufferType = SECBUFFER_TOKEN; chlg_buf.pvBuffer = chlg; chlg_buf.cbBuffer = curlx_uztoul(chlglen); } /* Setup the response "output" security buffer */ resp_desc.ulVersion = SECBUFFER_VERSION; resp_desc.cBuffers = 1; resp_desc.pBuffers = &resp_buf; resp_buf.BufferType = SECBUFFER_TOKEN; resp_buf.pvBuffer = nego->output_token; resp_buf.cbBuffer = curlx_uztoul(nego->token_max); /* Generate our challenge-response message */ nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials, chlg ? nego->context : NULL, nego->spn, ISC_REQ_CONFIDENTIALITY, 0, SECURITY_NATIVE_DREP, chlg ? &chlg_desc : NULL, 0, nego->context, &resp_desc, &attrs, &expiry); /* Free the decoded challenge as it is not required anymore */ free(chlg); if(GSS_ERROR(nego->status)) { return CURLE_OUT_OF_MEMORY; } if(nego->status == SEC_I_COMPLETE_NEEDED || nego->status == SEC_I_COMPLETE_AND_CONTINUE) { nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc); if(GSS_ERROR(nego->status)) { return CURLE_RECV_ERROR; } } nego->output_token_length = resp_buf.cbBuffer; return result; }
/* * Curl_ntlm_decode_type2_message() * * This is used to decode a ntlm type-2 message received from a: HTTP, SMTP * or POP3 server. The message is first decoded from a base64 string into a * raw ntlm message and checked for validity before the appropriate data for * creating a type-3 message is written to the given ntlm data structure. * * Parameters: * * data [in] - Pointer to session handle. * header [in] - Pointer to the input buffer. * ntlm [in] - Pointer to ntlm data struct being used and modified. * * Returns CURLE_OK on success. */ CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, const char* header, struct ntlmdata* ntlm) { #ifndef USE_WINDOWS_SSPI static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; #endif /* NTLM type-2 message structure: Index Description Content 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) 8 NTLM Message Type long (0x02000000) 12 Target Name security buffer 20 Flags long 24 Challenge 8 bytes (32) Context 8 bytes (two consecutive longs) (*) (40) Target Information security buffer (*) (48) OS Version Structure 8 bytes (*) 32 (48) (56) Start of data block (*) (*) -> Optional */ size_t size = 0; unsigned char *buffer = NULL; CURLcode error; #if defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_WINDOWS_SSPI) (void)data; #endif error = Curl_base64_decode(header, &buffer, &size); if(error) return error; if(!buffer) { infof(data, "NTLM handshake failure (unhandled condition)\n"); return CURLE_REMOTE_ACCESS_DENIED; } #ifdef USE_WINDOWS_SSPI ntlm->type_2 = malloc(size + 1); if(ntlm->type_2 == NULL) { free(buffer); return CURLE_OUT_OF_MEMORY; } ntlm->n_type_2 = (unsigned long)size; memcpy(ntlm->type_2, buffer, size); #else ntlm->flags = 0; if((size < 32) || (memcmp(buffer, NTLMSSP_SIGNATURE, 8) != 0) || (memcmp(buffer + 8, type2_marker, sizeof(type2_marker)) != 0)) { /* This was not a good enough type-2 message */ free(buffer); infof(data, "NTLM handshake failure (bad type-2 message)\n"); return CURLE_REMOTE_ACCESS_DENIED; } ntlm->flags = readint_le(&buffer[20]); memcpy(ntlm->nonce, &buffer[24], 8); DEBUG_OUT({ fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); ntlm_print_flags(stderr, ntlm->flags); fprintf(stderr, "\n nonce="); ntlm_print_hex(stderr, (char *)ntlm->nonce, 8); fprintf(stderr, "\n****\n"); fprintf(stderr, "**** Header %s\n ", header); });
/* * Curl_sasl_create_gssapi_security_message() * * This is used to generate an already encoded GSSAPI (Kerberos V5) security * token message ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - The optional base64 encoded challenge message. * krb5 [in/out] - The gssapi data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, const char *chlg64, struct kerberos5data *krb5, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t offset = 0; size_t chlglen = 0; size_t messagelen = 0; size_t appdatalen = 0; unsigned char *chlg = NULL; unsigned char *trailer = NULL; unsigned char *message = NULL; unsigned char *padding = NULL; unsigned char *appdata = NULL; SecBuffer input_buf[2]; SecBuffer wrap_buf[3]; SecBufferDesc input_desc; SecBufferDesc wrap_desc; unsigned long indata = 0; unsigned long outdata = 0; unsigned long qop = 0; unsigned long sec_layer = 0; unsigned long max_size = 0; SecPkgContext_Sizes sizes; SecPkgCredentials_Names names; SECURITY_STATUS status; char *user_name; /* Decode the base-64 encoded input message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "GSSAPI handshake failure (empty security message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Get our response size information */ status = s_pSecFn->QueryContextAttributes(krb5->context, SECPKG_ATTR_SIZES, &sizes); if(status != SEC_E_OK) { free(chlg); return CURLE_OUT_OF_MEMORY; } /* Get the fully qualified username back from the context */ status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials, SECPKG_CRED_ATTR_NAMES, &names); if(status != SEC_E_OK) { free(chlg); return CURLE_RECV_ERROR; } /* Setup the "input" security buffer */ input_desc.ulVersion = SECBUFFER_VERSION; input_desc.cBuffers = 2; input_desc.pBuffers = input_buf; input_buf[0].BufferType = SECBUFFER_STREAM; input_buf[0].pvBuffer = chlg; input_buf[0].cbBuffer = curlx_uztoul(chlglen); input_buf[1].BufferType = SECBUFFER_DATA; input_buf[1].pvBuffer = NULL; input_buf[1].cbBuffer = 0; /* Decrypt the inbound challenge and obtain the qop */ status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); if(status != SEC_E_OK) { infof(data, "GSSAPI handshake failure (empty security message)\n"); free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ if(input_buf[1].cbBuffer != 4) { infof(data, "GSSAPI handshake failure (invalid security data)\n"); free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Copy the data out and free the challenge as it is not required anymore */ memcpy(&indata, input_buf[1].pvBuffer, 4); s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); free(chlg); /* Extract the security layer */ sec_layer = indata & 0x000000FF; if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { infof(data, "GSSAPI handshake failure (invalid security layer)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Extract the maximum message size the server can receive */ max_size = ntohl(indata & 0xFFFFFF00); if(max_size > 0) { /* The server has told us it supports a maximum receive buffer, however, as we don't require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } /* Allocate the trailer */ trailer = malloc(sizes.cbSecurityTrailer); if(!trailer) return CURLE_OUT_OF_MEMORY; /* Convert the user name to UTF8 when operating with Unicode */ user_name = Curl_convert_tchar_to_UTF8(names.sUserName); if(!user_name) { free(trailer); return CURLE_OUT_OF_MEMORY; } /* Allocate our message */ messagelen = sizeof(outdata) + strlen(user_name) + 1; message = malloc(messagelen); if(!message) { free(trailer); Curl_unicodefree(user_name); return CURLE_OUT_OF_MEMORY; } /* Populate the message with the security layer, client supported receive message size and authorization identity including the 0x00 based terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization identity is not terminated with the zero-valued (%x00) octet." it seems necessary to include it. */ outdata = htonl(max_size) | sec_layer; memcpy(message, &outdata, sizeof(outdata)); strcpy((char *) message + sizeof(outdata), user_name); Curl_unicodefree(user_name); /* Allocate the padding */ padding = malloc(sizes.cbBlockSize); if(!padding) { free(message); free(trailer); return CURLE_OUT_OF_MEMORY; } /* Setup the "authentication data" security buffer */ wrap_desc.ulVersion = SECBUFFER_VERSION; wrap_desc.cBuffers = 3; wrap_desc.pBuffers = wrap_buf; wrap_buf[0].BufferType = SECBUFFER_TOKEN; wrap_buf[0].pvBuffer = trailer; wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer; wrap_buf[1].BufferType = SECBUFFER_DATA; wrap_buf[1].pvBuffer = message; wrap_buf[1].cbBuffer = curlx_uztoul(messagelen); wrap_buf[2].BufferType = SECBUFFER_PADDING; wrap_buf[2].pvBuffer = padding; wrap_buf[2].cbBuffer = sizes.cbBlockSize; /* Encrypt the data */ status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, &wrap_desc, 0); if(status != SEC_E_OK) { free(padding); free(message); free(trailer); return CURLE_OUT_OF_MEMORY; } /* Allocate the encryption (wrap) buffer */ appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + wrap_buf[2].cbBuffer; appdata = malloc(appdatalen); if(!appdata) { free(padding); free(message); free(trailer); return CURLE_OUT_OF_MEMORY; } /* Populate the encryption buffer */ memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); offset += wrap_buf[0].cbBuffer; memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); offset += wrap_buf[1].cbBuffer; memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); /* Base64 encode the response */ result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr, outlen); /* Free all of our local buffers */ free(appdata); free(padding); free(message); free(trailer); return result; }
CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, const char *header) { struct SessionHandle *data = conn->data; struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: &data->state.negotiate; OM_uint32 major_status, minor_status, discard_st; gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; size_t len; size_t rawlen = 0; CURLcode result; if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(data); return CURLE_LOGIN_DENIED; } if(!neg_ctx->server_name) { /* Generate our SPN */ char *spn = Curl_sasl_build_gssapi_spn("HTTP", proxy ? conn->proxy.name : conn->host.name); if(!spn) return CURLE_OUT_OF_MEMORY; /* Populate the SPN structure */ spn_token.value = spn; spn_token.length = strlen(spn); /* Import the SPN */ major_status = gss_import_name(&minor_status, &spn_token, GSS_C_NT_HOSTBASED_SERVICE, &neg_ctx->server_name); if(GSS_ERROR(major_status)) { Curl_gss_log_error(data, minor_status, "gss_import_name() failed: "); free(spn); return CURLE_OUT_OF_MEMORY; } free(spn); } header += strlen("Negotiate"); while(*header && ISSPACE(*header)) header++; len = strlen(header); if(len > 0) { result = Curl_base64_decode(header, (unsigned char **)&input_token.value, &rawlen); if(result) return result; if(!rawlen) { infof(data, "Negotiate handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } input_token.length = rawlen; DEBUGASSERT(input_token.value != NULL); } major_status = Curl_gss_init_sec_context(data, &minor_status, &neg_ctx->context, neg_ctx->server_name, &Curl_spnego_mech_oid, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, TRUE, NULL); Curl_safefree(input_token.value); neg_ctx->status = major_status; if(GSS_ERROR(major_status)) { if(output_token.value) gss_release_buffer(&discard_st, &output_token); Curl_gss_log_error(conn->data, minor_status, "gss_init_sec_context() failed: "); return CURLE_OUT_OF_MEMORY; } if(!output_token.value || !output_token.length) { if(output_token.value) gss_release_buffer(&discard_st, &output_token); return CURLE_OUT_OF_MEMORY; } neg_ctx->output_token = output_token; return CURLE_OK; }
/* * Curl_sasl_create_digest_md5_message() * * This is used to generate an already encoded DIGEST-MD5 response message * ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - The base64 encoded challenge message. * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, const char *chlg64, const char *userp, const char *passwdp, const char *service, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; TCHAR *spn = NULL; size_t chlglen = 0; size_t token_max = 0; unsigned char *input_token = NULL; unsigned char *output_token = NULL; CredHandle credentials; CtxtHandle context; PSecPkgInfo SecurityPackage; SEC_WINNT_AUTH_IDENTITY identity; SEC_WINNT_AUTH_IDENTITY *p_identity; SecBuffer chlg_buf; SecBuffer resp_buf; SecBufferDesc chlg_desc; SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ /* Decode the base-64 encoded challenge message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &input_token, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!input_token) { infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Query the security package for DigestSSP */ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), &SecurityPackage); if(status != SEC_E_OK) { free(input_token); return CURLE_NOT_BUILT_IN; } token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ s_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ output_token = malloc(token_max); if(!output_token) { free(input_token); return CURLE_OUT_OF_MEMORY; } /* Generate our SPN */ spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); if(!spn) { free(output_token); free(input_token); return CURLE_OUT_OF_MEMORY; } if(userp && *userp) { /* Populate our identity structure */ result = Curl_create_sspi_identity(userp, passwdp, &identity); if(result) { free(spn); free(output_token); free(input_token); return result; } /* Allow proper cleanup of the identity structure */ p_identity = &identity; } else /* Use the current Windows user */ p_identity = NULL; /* Acquire our credentials handle */ status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_DIGEST), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, &credentials, &expiry); if(status != SEC_E_OK) { Curl_sspi_free_identity(p_identity); free(spn); free(output_token); free(input_token); return CURLE_LOGIN_DENIED; } /* Setup the challenge "input" security buffer */ chlg_desc.ulVersion = SECBUFFER_VERSION; chlg_desc.cBuffers = 1; chlg_desc.pBuffers = &chlg_buf; chlg_buf.BufferType = SECBUFFER_TOKEN; chlg_buf.pvBuffer = input_token; chlg_buf.cbBuffer = curlx_uztoul(chlglen); /* Setup the response "output" security buffer */ resp_desc.ulVersion = SECBUFFER_VERSION; resp_desc.cBuffers = 1; resp_desc.pBuffers = &resp_buf; resp_buf.BufferType = SECBUFFER_TOKEN; resp_buf.pvBuffer = output_token; resp_buf.cbBuffer = curlx_uztoul(token_max); /* Generate our response message */ status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, 0, 0, 0, &chlg_desc, 0, &context, &resp_desc, &attrs, &expiry); if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { s_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); free(spn); free(output_token); free(input_token); return CURLE_RECV_ERROR; } /* Base64 encode the response */ result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer, outptr, outlen); /* Free our handles */ s_pSecFn->DeleteSecurityContext(&context); s_pSecFn->FreeCredentialsHandle(&credentials); /* Free the identity structure */ Curl_sspi_free_identity(p_identity); /* Free the SPN */ free(spn); /* Free the response buffer */ free(output_token); /* Free the decoded challenge message */ free(input_token); return result; }
/* returning zero (0) means success, everything else is treated as "failure" with no care exactly what the failure was */ int Curl_input_negotiate(struct connectdata *conn, bool proxy, const char *header) { struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: &conn->data->state.negotiate; BYTE *input_token = 0; SecBufferDesc out_buff_desc; SecBuffer out_sec_buff; SecBufferDesc in_buff_desc; SecBuffer in_sec_buff; unsigned long context_attributes; TimeStamp lifetime; TCHAR *sname; int ret; size_t len = 0, input_token_len = 0; bool gss = FALSE; const char* protocol; CURLcode error; while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; gss = TRUE; } else if(checkprefix("Negotiate", header)) { protocol = "Negotiate"; gss = FALSE; } else return -1; if(neg_ctx->context) { if(neg_ctx->gss != gss) { return -1; } } else { neg_ctx->protocol = protocol; neg_ctx->gss = gss; } if(neg_ctx->context && neg_ctx->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(conn->data); return -1; } if(0 == strlen(neg_ctx->server_name)) { ret = get_gss_name(conn, proxy, neg_ctx); if(ret) return ret; } if(!neg_ctx->output_token) { PSecPkgInfo SecurityPackage; ret = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT("Negotiate"), &SecurityPackage); if(ret != SEC_E_OK) return -1; /* Allocate input and output buffers according to the max token size as indicated by the security package */ neg_ctx->max_token_length = SecurityPackage->cbMaxToken; neg_ctx->output_token = malloc(neg_ctx->max_token_length); s_pSecFn->FreeContextBuffer(SecurityPackage); } /* Obtain the input token, if any */ header += strlen(neg_ctx->protocol); while(*header && ISSPACE(*header)) header++; len = strlen(header); if(!len) { /* first call in a new negotation, we have to acquire credentials, and allocate memory for the context */ neg_ctx->credentials = malloc(sizeof(CredHandle)); neg_ctx->context = malloc(sizeof(CtxtHandle)); if(!neg_ctx->credentials || !neg_ctx->context) return -1; neg_ctx->status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT("Negotiate"), SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, neg_ctx->credentials, &lifetime); if(neg_ctx->status != SEC_E_OK) return -1; } else { input_token = malloc(neg_ctx->max_token_length); if(!input_token) return -1; error = Curl_base64_decode(header, (unsigned char **)&input_token, &input_token_len); if(error || input_token_len == 0) return -1; } /* prepare the output buffers, and input buffers if present */ out_buff_desc.ulVersion = 0; out_buff_desc.cBuffers = 1; out_buff_desc.pBuffers = &out_sec_buff; out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->max_token_length); out_sec_buff.BufferType = SECBUFFER_TOKEN; out_sec_buff.pvBuffer = neg_ctx->output_token; if(input_token) { in_buff_desc.ulVersion = 0; in_buff_desc.cBuffers = 1; in_buff_desc.pBuffers = &in_sec_buff; in_sec_buff.cbBuffer = curlx_uztoul(input_token_len); in_sec_buff.BufferType = SECBUFFER_TOKEN; in_sec_buff.pvBuffer = input_token; } sname = Curl_convert_UTF8_to_tchar(neg_ctx->server_name); if(!sname) return CURLE_OUT_OF_MEMORY; neg_ctx->status = s_pSecFn->InitializeSecurityContext( neg_ctx->credentials, input_token ? neg_ctx->context : 0, sname, ISC_REQ_CONFIDENTIALITY, 0, SECURITY_NATIVE_DREP, input_token ? &in_buff_desc : 0, 0, neg_ctx->context, &out_buff_desc, &context_attributes, &lifetime); Curl_unicodefree(sname); if(GSS_ERROR(neg_ctx->status)) return -1; if(neg_ctx->status == SEC_I_COMPLETE_NEEDED || neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) { neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, &out_buff_desc); if(GSS_ERROR(neg_ctx->status)) return -1; } neg_ctx->output_token_length = out_sec_buff.cbBuffer; return 0; }
/* * Curl_sasl_create_gssapi_user_message() * * This is used to generate an already encoded GSSAPI (Kerberos V5) user token * message ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name in the format User or Domain\User. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * mutual_auth [in] - Flag specifing whether or not mutual authentication * is enabled. * chlg64 [in] - The optional base64 encoded challenge message. * krb5 [in/out] - The gssapi data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *service, const bool mutual_auth, const char *chlg64, struct kerberos5data *krb5, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlglen = 0; unsigned char *chlg = NULL; CtxtHandle context; PSecPkgInfo SecurityPackage; SecBuffer chlg_buf; SecBuffer resp_buf; SecBufferDesc chlg_desc; SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ if(!krb5->credentials) { /* Query the security package for Kerberos */ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_KERBEROS), &SecurityPackage); if(status != SEC_E_OK) { return CURLE_NOT_BUILT_IN; } krb5->token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ s_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ krb5->output_token = malloc(krb5->token_max); if(!krb5->output_token) return CURLE_OUT_OF_MEMORY; /* Generate our SPN */ krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); if(!krb5->spn) return CURLE_OUT_OF_MEMORY; if(userp && *userp) { /* Populate our identity structure */ result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); if(result) return result; /* Allow proper cleanup of the identity structure */ krb5->p_identity = &krb5->identity; } else /* Use the current Windows user */ krb5->p_identity = NULL; /* Allocate our credentials handle */ krb5->credentials = malloc(sizeof(CredHandle)); if(!krb5->credentials) return CURLE_OUT_OF_MEMORY; memset(krb5->credentials, 0, sizeof(CredHandle)); /* Acquire our credentials handle */ status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_KERBEROS), SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, krb5->credentials, &expiry); if(status != SEC_E_OK) return CURLE_LOGIN_DENIED; /* Allocate our new context handle */ krb5->context = malloc(sizeof(CtxtHandle)); if(!krb5->context) return CURLE_OUT_OF_MEMORY; memset(krb5->context, 0, sizeof(CtxtHandle)); } else { /* Decode the base-64 encoded challenge message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "GSSAPI handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Setup the challenge "input" security buffer */ chlg_desc.ulVersion = SECBUFFER_VERSION; chlg_desc.cBuffers = 1; chlg_desc.pBuffers = &chlg_buf; chlg_buf.BufferType = SECBUFFER_TOKEN; chlg_buf.pvBuffer = chlg; chlg_buf.cbBuffer = curlx_uztoul(chlglen); } /* Setup the response "output" security buffer */ resp_desc.ulVersion = SECBUFFER_VERSION; resp_desc.cBuffers = 1; resp_desc.pBuffers = &resp_buf; resp_buf.BufferType = SECBUFFER_TOKEN; resp_buf.pvBuffer = krb5->output_token; resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); /* Generate our challenge-response message */ status = s_pSecFn->InitializeSecurityContext(krb5->credentials, chlg ? krb5->context : NULL, krb5->spn, (mutual_auth ? ISC_REQ_MUTUAL_AUTH : 0), 0, SECURITY_NATIVE_DREP, chlg ? &chlg_desc : NULL, 0, &context, &resp_desc, &attrs, &expiry); if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { free(chlg); return CURLE_RECV_ERROR; } if(memcmp(&context, krb5->context, sizeof(context))) { s_pSecFn->DeleteSecurityContext(krb5->context); memcpy(krb5->context, &context, sizeof(context)); } if(resp_buf.cbBuffer) { /* Base64 encode the response */ result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer, resp_buf.cbBuffer, outptr, outlen); } /* Free the decoded challenge */ free(chlg); return result; }
CURLcode Curl_krb_kauth(struct connectdata *conn) { des_cblock key; des_key_schedule schedule; KTEXT_ST tkt, tktcopy; char *name; char *p; char passwd[100]; size_t tmp; ssize_t nread; int save; CURLcode result; unsigned char *ptr; save = Curl_set_command_prot(conn, prot_private); result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user); if(result) return result; result = Curl_GetFTPResponse(&nread, conn, NULL); if(result) return result; if(conn->data->state.buffer[0] != '3'){ Curl_set_command_prot(conn, save); return CURLE_FTP_WEIRD_SERVER_REPLY; } p = strstr(conn->data->state.buffer, "T="); if(!p) { Curl_failf(conn->data, "Bad reply from server"); Curl_set_command_prot(conn, save); return CURLE_FTP_WEIRD_SERVER_REPLY; } p += 2; tmp = Curl_base64_decode(p, &ptr); if(tmp >= sizeof(tkt.dat)) { free(ptr); tmp=0; } if(!tmp || !ptr) { Curl_failf(conn->data, "Failed to decode base64 in reply"); Curl_set_command_prot(conn, save); return CURLE_FTP_WEIRD_SERVER_REPLY; } memcpy((char *)tkt.dat, ptr, tmp); free(ptr); tkt.length = tmp; tktcopy.length = tkt.length; p = strstr(conn->data->state.buffer, "P="); if(!p) { Curl_failf(conn->data, "Bad reply from server"); Curl_set_command_prot(conn, save); return CURLE_FTP_WEIRD_SERVER_REPLY; } name = p + 2; for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++); *p = 0; des_string_to_key (conn->passwd, &key); des_key_sched(&key, schedule); des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat, tkt.length, schedule, &key, DES_DECRYPT); if(strcmp ((char*)tktcopy.dat + 8, KRB_TICKET_GRANTING_TICKET) != 0) { afs_string_to_key(passwd, krb_realmofhost(conn->host.name), &key); des_key_sched(&key, schedule); des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat, tkt.length, schedule, &key, DES_DECRYPT); } memset(key, 0, sizeof(key)); memset(schedule, 0, sizeof(schedule)); memset(passwd, 0, sizeof(passwd)); if(Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length, &p) < 1) { failf(conn->data, "Out of memory base64-encoding."); Curl_set_command_prot(conn, save); return CURLE_OUT_OF_MEMORY; } memset (tktcopy.dat, 0, tktcopy.length); result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p); free(p); if(result) return result; result = Curl_GetFTPResponse(&nread, conn, NULL); if(result) return result; Curl_set_command_prot(conn, save); return CURLE_OK; }