1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at http://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23#include "curl_setup.h" 24 25#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) 26 27#include "urldata.h" 28#include "sendf.h" 29#include "curl_gssapi.h" 30#include "rawstr.h" 31#include "curl_base64.h" 32#include "http_negotiate.h" 33#include "curl_sasl.h" 34#include "url.h" 35#include "curl_printf.h" 36 37/* The last #include files should be: */ 38#include "curl_memory.h" 39#include "memdebug.h" 40 41CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, 42 const char *header) 43{ 44 struct SessionHandle *data = conn->data; 45 struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: 46 &data->state.negotiate; 47 OM_uint32 major_status, minor_status, discard_st; 48 gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; 49 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; 50 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; 51 size_t len; 52 size_t rawlen = 0; 53 CURLcode result; 54 55 if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { 56 /* We finished successfully our part of authentication, but server 57 * rejected it (since we're again here). Exit with an error since we 58 * can't invent anything better */ 59 Curl_cleanup_negotiate(data); 60 return CURLE_LOGIN_DENIED; 61 } 62 63 if(!neg_ctx->server_name) { 64 /* Generate our SPN */ 65 char *spn = Curl_sasl_build_gssapi_spn( 66 proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] : 67 data->set.str[STRING_SERVICE_NAME], 68 proxy ? conn->proxy.name : conn->host.name); 69 if(!spn) 70 return CURLE_OUT_OF_MEMORY; 71 72 /* Populate the SPN structure */ 73 spn_token.value = spn; 74 spn_token.length = strlen(spn); 75 76 /* Import the SPN */ 77 major_status = gss_import_name(&minor_status, &spn_token, 78 GSS_C_NT_HOSTBASED_SERVICE, 79 &neg_ctx->server_name); 80 if(GSS_ERROR(major_status)) { 81 Curl_gss_log_error(data, minor_status, "gss_import_name() failed: "); 82 83 free(spn); 84 85 return CURLE_OUT_OF_MEMORY; 86 } 87 88 free(spn); 89 } 90 91 header += strlen("Negotiate"); 92 while(*header && ISSPACE(*header)) 93 header++; 94 95 len = strlen(header); 96 if(len > 0) { 97 result = Curl_base64_decode(header, (unsigned char **)&input_token.value, 98 &rawlen); 99 if(result) 100 return result; 101 102 if(!rawlen) { 103 infof(data, "Negotiate handshake failure (empty challenge message)\n"); 104 105 return CURLE_BAD_CONTENT_ENCODING; 106 } 107 108 input_token.length = rawlen; 109 110 DEBUGASSERT(input_token.value != NULL); 111 } 112 113 major_status = Curl_gss_init_sec_context(data, 114 &minor_status, 115 &neg_ctx->context, 116 neg_ctx->server_name, 117 &Curl_spnego_mech_oid, 118 GSS_C_NO_CHANNEL_BINDINGS, 119 &input_token, 120 &output_token, 121 TRUE, 122 NULL); 123 Curl_safefree(input_token.value); 124 125 neg_ctx->status = major_status; 126 if(GSS_ERROR(major_status)) { 127 if(output_token.value) 128 gss_release_buffer(&discard_st, &output_token); 129 Curl_gss_log_error(conn->data, minor_status, 130 "gss_init_sec_context() failed: "); 131 return CURLE_OUT_OF_MEMORY; 132 } 133 134 if(!output_token.value || !output_token.length) { 135 if(output_token.value) 136 gss_release_buffer(&discard_st, &output_token); 137 return CURLE_OUT_OF_MEMORY; 138 } 139 140 neg_ctx->output_token = output_token; 141 142 return CURLE_OK; 143} 144 145CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) 146{ 147 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 148 &conn->data->state.negotiate; 149 char *encoded = NULL; 150 size_t len = 0; 151 char *userp; 152 CURLcode result; 153 OM_uint32 discard_st; 154 155 result = Curl_base64_encode(conn->data, 156 neg_ctx->output_token.value, 157 neg_ctx->output_token.length, 158 &encoded, &len); 159 if(result) { 160 gss_release_buffer(&discard_st, &neg_ctx->output_token); 161 neg_ctx->output_token.value = NULL; 162 neg_ctx->output_token.length = 0; 163 return result; 164 } 165 166 if(!encoded || !len) { 167 gss_release_buffer(&discard_st, &neg_ctx->output_token); 168 neg_ctx->output_token.value = NULL; 169 neg_ctx->output_token.length = 0; 170 return CURLE_REMOTE_ACCESS_DENIED; 171 } 172 173 userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", 174 encoded); 175 if(proxy) { 176 Curl_safefree(conn->allocptr.proxyuserpwd); 177 conn->allocptr.proxyuserpwd = userp; 178 } 179 else { 180 Curl_safefree(conn->allocptr.userpwd); 181 conn->allocptr.userpwd = userp; 182 } 183 184 free(encoded); 185 186 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; 187} 188 189static void cleanup(struct negotiatedata *neg_ctx) 190{ 191 OM_uint32 minor_status; 192 if(neg_ctx->context != GSS_C_NO_CONTEXT) 193 gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER); 194 195 if(neg_ctx->output_token.value) 196 gss_release_buffer(&minor_status, &neg_ctx->output_token); 197 198 if(neg_ctx->server_name != GSS_C_NO_NAME) 199 gss_release_name(&minor_status, &neg_ctx->server_name); 200 201 memset(neg_ctx, 0, sizeof(*neg_ctx)); 202} 203 204void Curl_cleanup_negotiate(struct SessionHandle *data) 205{ 206 cleanup(&data->state.negotiate); 207 cleanup(&data->state.proxyneg); 208} 209 210#endif /* HAVE_GSSAPI && !CURL_DISABLE_HTTP && USE_SPNEGO */ 211