krb5-gssapi: Remove several memory leaks.
[platform/upstream/curl.git] / lib / krb5.c
1 /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
2  *
3  * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2010 Kungliga Tekniska Högskolan
4  * (Royal Institute of Technology, Stockholm, Sweden).
5  * Copyright (c) 2004 - 2009 Daniel Stenberg
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.  */
34
35 #include "setup.h"
36
37 #ifndef CURL_DISABLE_FTP
38 #ifdef HAVE_GSSAPI
39
40 #ifdef HAVE_OLD_GSSMIT
41 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
42 #endif
43
44 #include <stdlib.h>
45 #ifdef HAVE_NETDB_H
46 #include <netdb.h>
47 #endif
48 #include <string.h>
49
50 #ifdef HAVE_GSSGNU
51 #  include <gss.h>
52 #elif defined HAVE_GSSMIT
53    /* MIT style */
54 #  include <gssapi/gssapi.h>
55 #  include <gssapi/gssapi_generic.h>
56 #  include <gssapi/gssapi_krb5.h>
57 #else
58    /* Heimdal-style */
59 #  include <gssapi.h>
60 #endif
61
62 #include "urldata.h"
63 #include "curl_base64.h"
64 #include "ftp.h"
65 #include "sendf.h"
66 #include "krb4.h"
67 #include "curl_memory.h"
68
69 #define _MPRINTF_REPLACE /* use our functions only */
70 #include <curl/mprintf.h>
71
72 /* The last #include file should be: */
73 #include "memdebug.h"
74
75 #define LOCAL_ADDR (&conn->local_addr)
76 #define REMOTE_ADDR conn->ip_addr->ai_addr
77
78 static int
79 krb5_check_prot(void *app_data, int level)
80 {
81   app_data = NULL; /* prevent compiler warning */
82   if(level == prot_confidential)
83     return -1;
84   return 0;
85 }
86
87 static int
88 krb5_decode(void *app_data, void *buf, int len, int level,
89             struct connectdata *conn)
90 {
91   gss_ctx_id_t *context = app_data;
92   OM_uint32 maj, min;
93   gss_buffer_desc enc, dec;
94
95   /* shut gcc up */
96   level = 0;
97   conn = NULL;
98
99   enc.value = buf;
100   enc.length = len;
101   maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
102   if(maj != GSS_S_COMPLETE) {
103     if(len >= 4)
104       strcpy(buf, "599 ");
105     return -1;
106   }
107
108   memcpy(buf, dec.value, dec.length);
109   len = dec.length;
110   gss_release_buffer(&min, &dec);
111
112   return len;
113 }
114
115 static int
116 krb5_overhead(void *app_data, int level, int len)
117 {
118   /* no arguments are used, just init them to prevent compiler warnings */
119   app_data = NULL;
120   level = 0;
121   len = 0;
122   return 0;
123 }
124
125 static int
126 krb5_encode(void *app_data, const void *from, int length, int level, void **to,
127             struct connectdata *conn)
128 {
129   gss_ctx_id_t *context = app_data;
130   gss_buffer_desc dec, enc;
131   OM_uint32 maj, min;
132   int state;
133   int len;
134
135   /* shut gcc up */
136   conn = NULL;
137
138   /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
139    * libraries modify the input buffer in gss_seal()
140    */
141   dec.value = (void*)from;
142   dec.length = length;
143   maj = gss_seal(&min, *context,
144                  level == prot_private,
145                  GSS_C_QOP_DEFAULT,
146                  &dec, &state, &enc);
147
148   if(maj != GSS_S_COMPLETE)
149     return -1;
150
151   /* malloc a new buffer, in case gss_release_buffer doesn't work as expected */
152   *to = malloc(enc.length);
153   if(!*to)
154     return -1;
155   memcpy(*to, enc.value, enc.length);
156   len = enc.length;
157   gss_release_buffer(&min, &enc);
158   return len;
159 }
160
161 static int
162 krb5_auth(void *app_data, struct connectdata *conn)
163 {
164   int ret;
165   char *p;
166   const char *host = conn->host.name;
167   ssize_t nread;
168   curl_socklen_t l = sizeof(conn->local_addr);
169   struct SessionHandle *data = conn->data;
170   CURLcode result;
171   const char *service = "ftp", *srv_host = "host";
172   gss_buffer_desc gssbuf, _gssresp, *gssresp;
173   OM_uint32 maj, min;
174   gss_name_t gssname;
175   gss_ctx_id_t *context = app_data;
176   struct gss_channel_bindings_struct chan;
177
178   if(getsockname(conn->sock[FIRSTSOCKET],
179                  (struct sockaddr *)LOCAL_ADDR, &l) < 0)
180     perror("getsockname()");
181
182   chan.initiator_addrtype = GSS_C_AF_INET;
183   chan.initiator_address.length = l - 4;
184   chan.initiator_address.value =
185     &((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr;
186   chan.acceptor_addrtype = GSS_C_AF_INET;
187   chan.acceptor_address.length = l - 4;
188   chan.acceptor_address.value =
189     &((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr;
190   chan.application_data.length = 0;
191   chan.application_data.value = NULL;
192
193   /* this loop will execute twice (once for service, once for host) */
194   while(1) {
195     /* this really shouldn't be repeated here, but can't help it */
196     if(service == srv_host) {
197       result = Curl_ftpsendf(conn, "AUTH GSSAPI");
198
199       if(result)
200         return -2;
201       if(Curl_GetFTPResponse(&nread, conn, NULL))
202         return -1;
203
204       if(data->state.buffer[0] != '3')
205         return -1;
206     }
207
208     gssbuf.value = data->state.buffer;
209     gssbuf.length = snprintf(gssbuf.value, BUFSIZE, "%s@%s", service, host);
210     maj = gss_import_name(&min, &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &gssname);
211     if(maj != GSS_S_COMPLETE) {
212       gss_release_name(&min, &gssname);
213       if(service == srv_host) {
214         Curl_failf(data, "Error importing service name %s", gssbuf.value);
215         return AUTH_ERROR;
216       }
217       service = srv_host;
218       continue;
219     }
220     {
221       /* We pass NULL as |output_name_type| to avoid a leak. */
222       gss_display_name(&min, gssname, &gssbuf, NULL);
223       Curl_infof(data, "Trying against %s\n", gssbuf.value);
224       gss_release_buffer(&min, &gssbuf);
225     }
226     gssresp = GSS_C_NO_BUFFER;
227     *context = GSS_C_NO_CONTEXT;
228
229     do {
230       ret = AUTH_OK;
231       maj = gss_init_sec_context(&min,
232                                  GSS_C_NO_CREDENTIAL,
233                                  context,
234                                  gssname,
235                                  GSS_C_NO_OID,
236                                  GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
237                                  0,
238                                  &chan,
239                                  gssresp,
240                                  NULL,
241                                  &gssbuf,
242                                  NULL,
243                                  NULL);
244
245       if(gssresp) {
246         free(_gssresp.value);
247         gssresp = NULL;
248       }
249
250       if(maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
251         Curl_infof(data, "Error creating security context");
252         ret = AUTH_ERROR;
253         break;
254       }
255
256       if(gssbuf.length != 0) {
257         if(Curl_base64_encode(data, (char *)gssbuf.value, gssbuf.length, &p)
258            < 1) {
259           Curl_infof(data, "Out of memory base64-encoding");
260           ret = AUTH_CONTINUE;
261           break;
262         }
263
264         result = Curl_ftpsendf(conn, "ADAT %s", p);
265
266         free(p);
267
268         if(result) {
269           ret = -2;
270           break;
271         }
272
273         if(Curl_GetFTPResponse(&nread, conn, NULL)) {
274           ret = -1;
275           break;
276         }
277
278         if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3'){
279           Curl_infof(data, "Server didn't accept auth data\n");
280           ret = AUTH_ERROR;
281           break;
282         }
283
284         p = data->state.buffer + 4;
285         p = strstr(p, "ADAT=");
286         if(p) {
287           _gssresp.length = Curl_base64_decode(p + 5, (unsigned char **)
288                                                &_gssresp.value);
289           if(_gssresp.length < 1) {
290             Curl_failf(data, "Out of memory base64-encoding");
291             ret = AUTH_CONTINUE;
292             break;
293           }
294         }
295
296         gssresp = &_gssresp;
297       }
298     } while(maj == GSS_S_CONTINUE_NEEDED);
299
300     gss_release_name(&min, &gssname);
301
302     if(gssresp)
303       free(_gssresp.value);
304
305     if(ret == AUTH_OK || service == srv_host)
306       return ret;
307
308     service = srv_host;
309   }
310 }
311
312 struct Curl_sec_client_mech Curl_krb5_client_mech = {
313     "GSSAPI",
314     sizeof(gss_ctx_id_t),
315     NULL, /* init */
316     krb5_auth,
317     NULL, /* end */
318     krb5_check_prot,
319     krb5_overhead,
320     krb5_encode,
321     krb5_decode
322 };
323
324 #endif /* HAVE_GSSAPI */
325 #endif /* CURL_DISABLE_FTP */