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_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; }
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct connectdata *conn) { struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[sockindex]; CURLcode code; ssize_t actualread; ssize_t written; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc* gss_token = GSS_C_NO_BUFFER; gss_name_t server = GSS_C_NO_NAME; gss_name_t gss_client_name = GSS_C_NO_NAME; unsigned short us_length; char *user=NULL; unsigned char socksreq[4]; /* room for gssapi exchange header only */ char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE]; /* GSSAPI request looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ /* prepare service name */ if(strchr(serviceptr,'/')) { service.value = malloc(strlen(serviceptr)); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr); memcpy(service.value, serviceptr, service.length); gss_major_status = gss_import_name(&gss_minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server); } else { service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1; snprintf(service.value, service.length+1, "%s@%s", serviceptr, conn->proxy.name); gss_major_status = gss_import_name(&gss_minor_status, &service, gss_nt_service_name, &server); } gss_release_buffer(&gss_status, &service); /* clear allocated memory */ if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_import_name()")) { failf(data, "Failed to create service name."); gss_release_name(&gss_status, &server); return CURLE_COULDNT_CONNECT; } /* As long as we need to keep sending some context info, and there's no */ /* errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, &gss_context, server, NULL, gss_token, &gss_send_token, &gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) gss_release_buffer(&gss_status, &gss_recv_token); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_init_sec_context")) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to initial GSSAPI token."); return CURLE_COULDNT_CONNECT; } if(gss_send_token.length != 0) { socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 1; /* authentication message type */ us_length = htons((short)gss_send_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } code = Curl_write_plain(conn, sock, (char *)gss_send_token.value, gss_send_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) { failf(data, "Failed to send GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_recv_token); if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; /* analyse response */ /* GSSAPI response looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI authentication response."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / messgae type */ failf(data, "Invalid GSSAPI authentication response type (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length=us_length; gss_recv_token.value=malloc(us_length); if(!gss_recv_token.value) { failf(data, "Could not allocate memory for GSSAPI authentication " "response token."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } gss_release_name(&gss_status, &server); /* Everything is good so far, user was authenticated! */ gss_major_status = gss_inquire_context (&gss_minor_status, gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_inquire_context")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, &gss_send_token, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_display_name")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } user=malloc(gss_send_token.length+1); if(!user) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); return CURLE_OUT_OF_MEMORY; } memcpy(user, gss_send_token.value, gss_send_token.length); user[gss_send_token.length] = '\0'; gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user); free(user); user=NULL; /* Do encryption */ socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 2; /* encryption message type */ gss_enc = 0; /* no data protection */ /* do confidentiality protection if supported */ if(gss_ret_flags & GSS_C_CONF_FLAG) gss_enc = 2; /* else do integrity protection */ else if(gss_ret_flags & GSS_C_INTEG_FLAG) gss_enc = 1; infof(data, "SOCKS5 server supports gssapi %s data protection.\n", (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality")); /* force for the moment to no data protection */ gss_enc = 0; /* * Sending the encryption type in clear seems wrong. It should be * protected with gss_seal()/gss_wrap(). See RFC1961 extract below * The NEC reference implementations on which this is based is * therefore at fault * * +------+------+------+.......................+ * + ver | mtyp | len | token | * +------+------+------+.......................+ * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | * +------+------+------+.......................+ * * Where: * * - "ver" is the protocol version number, here 1 to represent the * first version of the SOCKS/GSS-API protocol * * - "mtyp" is the message type, here 2 to represent a protection * -level negotiation message * * - "len" is the length of the "token" field in octets * * - "token" is the GSS-API encapsulated protection level * * The token is produced by encapsulating an octet containing the * required protection level using gss_seal()/gss_wrap() with conf_req * set to FALSE. The token is verified using gss_unseal()/ * gss_unwrap(). * */ if(data->set.socks5_gssapi_nec) { us_length = htons((short)1); memcpy(socksreq+2,&us_length,sizeof(short)); } else { gss_send_token.length = 1; gss_send_token.value = malloc(1); if(!gss_send_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } memcpy(gss_send_token.value, &gss_enc, 1); gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, &gss_conf_state, &gss_w_token); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) { gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_send_token); us_length = htons((short)gss_w_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); } code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); code = Curl_write_plain(conn, sock, socksreq, 1, &written); if((code != CURLE_OK) || ( 1 != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { code = Curl_write_plain(conn, sock, (char *)gss_w_token.value, gss_w_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); } result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / messgae type */ failf(data, "Invalid GSSAPI encryption response type (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length= us_length; gss_recv_token.value=malloc(gss_recv_token.length); if(!gss_recv_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI encryptrion type."); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { gss_major_status = gss_unwrap(&gss_minor_status, gss_context, &gss_recv_token, &gss_w_token, 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) { gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_recv_token); if(gss_w_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_w_token.value,gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); } else { if(gss_recv_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_recv_token.value,gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); } infof(data, "SOCKS5 access with%s protection granted.\n", (socksreq[0]==0)?"out gssapi data": ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality")); conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_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, 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, rawlen; 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 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) { 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; if(input_token.value == NULL) return CURLE_OUT_OF_MEMORY; spnegoToken = malloc(input_token.length); if(spnegoToken == NULL) return CURLE_OUT_OF_MEMORY; 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(data, "Parse SPNEGO Target Token failed\n"); } else { free(input_token.value); input_token.value = malloc(mechTokenLength); if(input_token.value == NULL) return CURLE_OUT_OF_MEMORY; memcpy(input_token.value, mechToken,mechTokenLength); input_token.length = mechTokenLength; free(mechToken); mechToken = NULL; infof(data, "Parse SPNEGO Target Token succeeded\n"); } } #endif } major_status = Curl_gss_init_sec_context(data, &minor_status, &neg_ctx->context, neg_ctx->server_name, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, 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(data) ??? */ log_gss_error(conn, minor_status, "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; }
/* 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; }
/* * 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; }
/* * Curl_auth_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. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * host [in[ - The host name. * mutual_auth [in] - Flag specifying whether or not mutual authentication * is enabled. * chlg64 [in] - Pointer to the optional base64 encoded challenge * message. * krb5 [in/out] - The Kerberos 5 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_auth_create_gssapi_user_message(struct Curl_easy *data, const char *userp, const char *passwdp, const char *service, const char *host, 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 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) userp; (void) passwdp; if(!krb5->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, &krb5->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, "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; } major_status = Curl_gss_init_sec_context(data, &minor_status, &krb5->context, krb5->spn, &Curl_krb5_mech_oid, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, mutual_auth, NULL); /* Free the decoded challenge as it is not required anymore */ free(input_token.value); 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_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(&unused_status, &output_token); } else if(mutual_auth) { *outptr = strdup(""); if(!*outptr) result = CURLE_OUT_OF_MEMORY; } return result; }