Fix a couple of spelling errors in lib/
[platform/upstream/curl.git] / lib / http_negotiate_sspi.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, 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 #include "setup.h"
23
24 #ifdef USE_WINDOWS_SSPI
25
26 #ifndef CURL_DISABLE_HTTP
27 /* -- WIN32 approved -- */
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33
34 #include "urldata.h"
35 #include "sendf.h"
36 #include "rawstr.h"
37 #include "curl_base64.h"
38 #include "http_negotiate.h"
39 #include "curl_memory.h"
40
41 #define _MPRINTF_REPLACE /* use our functions only */
42 #include <curl/mprintf.h>
43
44 /* The last #include file should be: */
45 #include "memdebug.h"
46
47 static int
48 get_gss_name(struct connectdata *conn, bool proxy, char *server)
49 {
50   struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
51     &conn->data->state.negotiate;
52   const char* service;
53   size_t length;
54
55   /* GSSAPI implementation by Globus (known as GSI) requires the name to be
56      of form "<service>/<fqdn>" instead of <service>@<fqdn> (ie. slash instead
57      of at-sign). Also GSI servers are often identified as 'host' not 'khttp'.
58      Change following lines if you want to use GSI */
59
60   /* IIS uses the <service>@<fqdn> form but uses 'http' as the service name,
61      and SSPI then generates an NTLM token. When using <service>/<fqdn> a
62      Kerberos token is generated. */
63
64   if(neg_ctx->gss)
65     service = "KHTTP";
66   else
67     service = "HTTP";
68
69   length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name :
70                                         conn->host.name) + 1;
71   if(length + 1 > sizeof(neg_ctx->server_name))
72     return EMSGSIZE;
73
74   snprintf(server, sizeof(neg_ctx->server_name), "%s/%s",
75            service, proxy ? conn->proxy.name : conn->host.name);
76
77   return 0;
78 }
79
80 /* returning zero (0) means success, everything else is treated as "failure"
81    with no care exactly what the failure was */
82 int Curl_input_negotiate(struct connectdata *conn, bool proxy,
83                          const char *header)
84 {
85   struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
86     &conn->data->state.negotiate;
87   BYTE                          *input_token = 0;
88   SecBufferDesc     out_buff_desc;
89   SecBuffer         out_sec_buff;
90   SecBufferDesc     in_buff_desc;
91   SecBuffer         in_sec_buff;
92   ULONG             context_attributes;
93   TimeStamp         lifetime;
94
95   int ret;
96   size_t len = 0, input_token_len = 0;
97   bool gss = FALSE;
98   const char* protocol;
99
100   while(*header && ISSPACE(*header))
101     header++;
102
103   if(checkprefix("GSS-Negotiate", header)) {
104     protocol = "GSS-Negotiate";
105     gss = TRUE;
106   }
107   else if(checkprefix("Negotiate", header)) {
108     protocol = "Negotiate";
109     gss = FALSE;
110   }
111   else
112     return -1;
113
114   if(neg_ctx->context) {
115     if(neg_ctx->gss != gss) {
116       return -1;
117     }
118   }
119   else {
120     neg_ctx->protocol = protocol;
121     neg_ctx->gss = gss;
122   }
123
124   if(neg_ctx->context && neg_ctx->status == SEC_E_OK) {
125     /* We finished successfully our part of authentication, but server
126      * rejected it (since we're again here). Exit with an error since we
127      * can't invent anything better */
128     Curl_cleanup_negotiate(conn->data);
129     return -1;
130   }
131
132   if(strlen(neg_ctx->server_name) == 0 &&
133      (ret = get_gss_name(conn, proxy, neg_ctx->server_name)))
134     return ret;
135
136   if (!neg_ctx->max_token_length) {
137     PSecPkgInfo SecurityPackage;
138     ret = s_pSecFn->QuerySecurityPackageInfo((SEC_CHAR *)"Negotiate",
139                                              &SecurityPackage);
140     if (ret != SEC_E_OK)
141       return -1;
142
143     /* Allocate input and output buffers according to the max token size
144        as indicated by the security package */
145     neg_ctx->max_token_length = SecurityPackage->cbMaxToken;
146     neg_ctx->output_token = (BYTE *)malloc(neg_ctx->max_token_length);
147     s_pSecFn->FreeContextBuffer(SecurityPackage);
148   }
149
150   /* Obtain the input token, if any */
151   header += strlen(neg_ctx->protocol);
152   while(*header && ISSPACE(*header))
153     header++;
154
155   len = strlen(header);
156   if(len > 0) {
157     input_token = malloc(neg_ctx->max_token_length);
158     if(!input_token)
159       return -1;
160
161     input_token_len = Curl_base64_decode(header,
162                                          (unsigned char **)&input_token);
163     if(input_token_len == 0)
164       return -1;
165   }
166
167   if ( !input_token ) {
168     /* first call in a new negotiation, we have to require credentials,
169        and allocate memory for the context */
170
171     neg_ctx->credentials = (CredHandle *)malloc(sizeof(CredHandle));
172     neg_ctx->context = (CtxtHandle *)malloc(sizeof(CtxtHandle));
173
174     if ( !neg_ctx->credentials || !neg_ctx->context)
175       return -1;
176
177     neg_ctx->status =
178       s_pSecFn->AcquireCredentialsHandle(NULL, (SEC_CHAR *)"Negotiate",
179                                          SECPKG_CRED_OUTBOUND, NULL, NULL,
180                                          NULL, NULL, neg_ctx->credentials,
181                                          &lifetime);
182     if ( neg_ctx->status != SEC_E_OK )
183       return -1;
184   }
185
186   /* prepare the output buffers, and input buffers if present */
187   out_buff_desc.ulVersion = 0;
188   out_buff_desc.cBuffers  = 1;
189   out_buff_desc.pBuffers  = &out_sec_buff;
190
191   out_sec_buff.cbBuffer   = neg_ctx->max_token_length;
192   out_sec_buff.BufferType = SECBUFFER_TOKEN;
193   out_sec_buff.pvBuffer   = neg_ctx->output_token;
194
195
196   if (input_token) {
197     in_buff_desc.ulVersion = 0;
198     in_buff_desc.cBuffers  = 1;
199     in_buff_desc.pBuffers  = &out_sec_buff;
200
201     in_sec_buff.cbBuffer   = input_token_len;
202     in_sec_buff.BufferType = SECBUFFER_TOKEN;
203     in_sec_buff.pvBuffer   = input_token;
204   }
205
206   neg_ctx->status = s_pSecFn->InitializeSecurityContext(
207     neg_ctx->credentials,
208     input_token ? neg_ctx->context : 0,
209     neg_ctx->server_name,
210     ISC_REQ_CONFIDENTIALITY,
211     0,
212     SECURITY_NATIVE_DREP,
213     input_token ? &in_buff_desc : 0,
214     0,
215     neg_ctx->context,
216     &out_buff_desc,
217     &context_attributes,
218     &lifetime);
219
220   if ( GSS_ERROR(neg_ctx->status) )
221     return -1;
222
223   if ( neg_ctx->status == SEC_I_COMPLETE_NEEDED ||
224        neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE ) {
225     neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context,
226                                                   &out_buff_desc);
227     if ( GSS_ERROR(neg_ctx->status) )
228       return -1;
229   }
230
231   neg_ctx->output_token_length = out_sec_buff.cbBuffer;
232
233   return 0;
234 }
235
236
237 CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
238 {
239   struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
240     &conn->data->state.negotiate;
241   char *encoded = NULL;
242   size_t len;
243   char *userp;
244
245   len = Curl_base64_encode(conn->data,
246                            (const char*)neg_ctx->output_token,
247                            neg_ctx->output_token_length,
248                            &encoded);
249
250   if(len == 0)
251     return CURLE_OUT_OF_MEMORY;
252
253   userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
254                   neg_ctx->protocol, encoded);
255
256   if(proxy)
257     conn->allocptr.proxyuserpwd = userp;
258   else
259     conn->allocptr.userpwd = userp;
260   free(encoded);
261   Curl_cleanup_negotiate (conn->data);
262   return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
263 }
264
265 static void cleanup(struct negotiatedata *neg_ctx)
266 {
267   if(neg_ctx->context) {
268     s_pSecFn->DeleteSecurityContext(neg_ctx->context);
269     free(neg_ctx->context);
270     neg_ctx->context = 0;
271   }
272
273   if(neg_ctx->credentials) {
274     s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials);
275     free(neg_ctx->credentials);
276     neg_ctx->credentials = 0;
277   }
278
279   if(neg_ctx->output_token) {
280     free(neg_ctx->output_token);
281     neg_ctx->output_token = 0;
282   }
283 }
284
285 void Curl_cleanup_negotiate(struct SessionHandle *data)
286 {
287   cleanup(&data->state.negotiate);
288   cleanup(&data->state.proxyneg);
289 }
290
291
292 #endif
293 #endif