00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "setup.h"
00024
00025 #ifdef HAVE_GSSAPI
00026 #ifdef HAVE_GSSMIT
00027 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
00028 #endif
00029
00030 #ifndef CURL_DISABLE_HTTP
00031
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <stdarg.h>
00035 #include <stdlib.h>
00036 #include <ctype.h>
00037
00038 #include "urldata.h"
00039 #include "sendf.h"
00040 #include "strequal.h"
00041 #include "base64.h"
00042 #include "http_negotiate.h"
00043 #include "memory.h"
00044
00045 #define _MPRINTF_REPLACE
00046 #include <curl/mprintf.h>
00047
00048
00049 #include "memdebug.h"
00050
00051 static int
00052 get_gss_name(struct connectdata *conn, gss_name_t *server)
00053 {
00054 struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
00055 OM_uint32 major_status, minor_status;
00056 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
00057 char name[2048];
00058 const char* service;
00059
00060
00061
00062
00063
00064
00065
00066
00067 if (neg_ctx->gss)
00068 service = "KHTTP";
00069 else
00070 service = "HTTP";
00071
00072 token.length = strlen(service) + 1 + strlen(conn->host.name) + 1;
00073 if (token.length + 1 > sizeof(name))
00074 return EMSGSIZE;
00075
00076 snprintf(name, sizeof(name), "%s@%s", service, conn->host.name);
00077
00078 token.value = (void *) name;
00079 major_status = gss_import_name(&minor_status,
00080 &token,
00081 GSS_C_NT_HOSTBASED_SERVICE,
00082 server);
00083
00084 return GSS_ERROR(major_status) ? -1 : 0;
00085 }
00086
00087 static void
00088 log_gss_error(struct connectdata *conn, OM_uint32 error_status, char *prefix)
00089 {
00090 OM_uint32 maj_stat, min_stat;
00091 OM_uint32 msg_ctx = 0;
00092 gss_buffer_desc status_string;
00093 char buf[1024];
00094 size_t len;
00095
00096 snprintf(buf, sizeof(buf), "%s", prefix);
00097 len = strlen(buf);
00098 do {
00099 maj_stat = gss_display_status (&min_stat,
00100 error_status,
00101 GSS_C_MECH_CODE,
00102 GSS_C_NO_OID,
00103 &msg_ctx,
00104 &status_string);
00105 if (sizeof(buf) > len + status_string.length + 1) {
00106 snprintf(buf + len, sizeof(buf) - len,
00107 ": %s", (char*) status_string.value);
00108 len += status_string.length;
00109 }
00110 gss_release_buffer(&min_stat, &status_string);
00111 } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
00112
00113 infof(conn->data, "%s", buf);
00114 }
00115
00116 int Curl_input_negotiate(struct connectdata *conn, char *header)
00117 {
00118 struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
00119 OM_uint32 major_status, minor_status, minor_status2;
00120 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
00121 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
00122 int ret;
00123 size_t len;
00124 bool gss;
00125 const char* protocol;
00126
00127 while(*header && ISSPACE(*header))
00128 header++;
00129 if(checkprefix("GSS-Negotiate", header)) {
00130 protocol = "GSS-Negotiate";
00131 gss = TRUE;
00132 }
00133 else if (checkprefix("Negotiate", header)) {
00134 protocol = "Negotiate";
00135 gss = FALSE;
00136 }
00137 else
00138 return -1;
00139
00140 if (neg_ctx->context) {
00141 if (neg_ctx->gss != gss) {
00142 return -1;
00143 }
00144 }
00145 else {
00146 neg_ctx->protocol = protocol;
00147 neg_ctx->gss = gss;
00148 }
00149
00150 if (neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
00151
00152
00153
00154 Curl_cleanup_negotiate(conn->data);
00155 return -1;
00156 }
00157
00158 if (neg_ctx->server_name == NULL &&
00159 (ret = get_gss_name(conn, &neg_ctx->server_name)))
00160 return ret;
00161
00162 header += strlen(neg_ctx->protocol);
00163 while(*header && ISSPACE(*header))
00164 header++;
00165
00166 len = strlen(header);
00167 if (len > 0) {
00168 int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value);
00169 if (rawlen < 0)
00170 return -1;
00171 input_token.length = rawlen;
00172
00173 #ifdef HAVE_SPNEGO
00174 if (checkprefix("Negotiate", header)) {
00175 ASN1_OBJECT * object = NULL;
00176 int rc = 1;
00177 unsigned char * spnegoToken = NULL;
00178 size_t spnegoTokenLength = 0;
00179 unsigned char * mechToken = NULL;
00180 size_t mechTokenLength = 0;
00181
00182 spnegoToken = malloc(input_token.length);
00183 if (input_token.value == NULL)
00184 return ENOMEM;
00185 spnegoTokenLength = input_token.length;
00186
00187 object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
00188 if (!parseSpnegoTargetToken(spnegoToken,
00189 spnegoTokenLength,
00190 NULL,
00191 NULL,
00192 &mechToken,
00193 &mechTokenLength,
00194 NULL,
00195 NULL)) {
00196 free(spnegoToken);
00197 spnegoToken = NULL;
00198 infof(conn->data, "Parse SPNEGO Target Token failed\n");
00199 }
00200 else {
00201 free(input_token.value);
00202 input_token.value = NULL;
00203 input_token.value = malloc(mechTokenLength);
00204 memcpy(input_token.value, mechToken,mechTokenLength);
00205 input_token.length = mechTokenLength;
00206 free(mechToken);
00207 mechToken = NULL;
00208 infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
00209 }
00210 }
00211 #endif
00212 }
00213
00214 major_status = gss_init_sec_context(&minor_status,
00215 GSS_C_NO_CREDENTIAL,
00216 &neg_ctx->context,
00217 neg_ctx->server_name,
00218 GSS_C_NO_OID,
00219 GSS_C_DELEG_FLAG,
00220 0,
00221 GSS_C_NO_CHANNEL_BINDINGS,
00222 &input_token,
00223 NULL,
00224 &output_token,
00225 NULL,
00226 NULL);
00227 if (input_token.length > 0)
00228 gss_release_buffer(&minor_status2, &input_token);
00229 neg_ctx->status = major_status;
00230 if (GSS_ERROR(major_status)) {
00231
00232 log_gss_error(conn, minor_status,
00233 (char *)"gss_init_sec_context() failed: ");
00234 return -1;
00235 }
00236
00237 if (output_token.length == 0) {
00238 return -1;
00239 }
00240
00241 neg_ctx->output_token = output_token;
00242
00243
00244 return 0;
00245 }
00246
00247
00248 CURLcode Curl_output_negotiate(struct connectdata *conn)
00249 {
00250 struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
00251 OM_uint32 minor_status;
00252 char *encoded = NULL;
00253 int len;
00254
00255 #ifdef HAVE_SPNEGO
00256 if (checkprefix("Negotiate",neg_ctx->protocol)) {
00257 ASN1_OBJECT * object = NULL;
00258 int rc = 1;
00259 unsigned char * spnegoToken = NULL;
00260 size_t spnegoTokenLength = 0;
00261 unsigned char * responseToken = NULL;
00262 size_t responseTokenLength = 0;
00263
00264 responseToken = malloc(neg_ctx->output_token.length);
00265 if ( responseToken == NULL)
00266 return CURLE_OUT_OF_MEMORY;
00267 memcpy(responseToken, neg_ctx->output_token.value,
00268 neg_ctx->output_token.length);
00269 responseTokenLength = neg_ctx->output_token.length;
00270
00271 object=OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
00272 if (!makeSpnegoInitialToken (object,
00273 responseToken,
00274 responseTokenLength,
00275 &spnegoToken,
00276 &spnegoTokenLength)) {
00277 free(responseToken);
00278 responseToken = NULL;
00279 infof(conn->data, "Make SPNEGO Initial Token failed\n");
00280 }
00281 else {
00282 free(neg_ctx->output_token.value);
00283 responseToken = NULL;
00284 neg_ctx->output_token.value = malloc(spnegoTokenLength);
00285 memcpy(neg_ctx->output_token.value, spnegoToken,spnegoTokenLength);
00286 neg_ctx->output_token.length = spnegoTokenLength;
00287 free(spnegoToken);
00288 spnegoToken = NULL;
00289 infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
00290 }
00291 }
00292 #endif
00293 len = Curl_base64_encode(conn->data,
00294 neg_ctx->output_token.value,
00295 neg_ctx->output_token.length,
00296 &encoded);
00297
00298 if (len == 0)
00299 return CURLE_OUT_OF_MEMORY;
00300
00301 conn->allocptr.userpwd =
00302 aprintf("Authorization: %s %s\r\n", neg_ctx->protocol, encoded);
00303 free(encoded);
00304 gss_release_buffer(&minor_status, &neg_ctx->output_token);
00305 return (conn->allocptr.userpwd == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
00306 }
00307
00308 void Curl_cleanup_negotiate(struct SessionHandle *data)
00309 {
00310 OM_uint32 minor_status;
00311 struct negotiatedata *neg_ctx = &data->state.negotiate;
00312
00313 if (neg_ctx->context != GSS_C_NO_CONTEXT)
00314 gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);
00315
00316 if (neg_ctx->output_token.length != 0)
00317 gss_release_buffer(&minor_status, &neg_ctx->output_token);
00318
00319 if (neg_ctx->server_name != GSS_C_NO_NAME)
00320 gss_release_name(&minor_status, &neg_ctx->server_name);
00321
00322 memset(neg_ctx, 0, sizeof(*neg_ctx));
00323 }
00324
00325
00326 #endif
00327 #endif