Annotation of embedaddon/curl/lib/krb5.c, revision 1.1.1.1
1.1 misho 1: /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
2: *
3: * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
4: * (Royal Institute of Technology, Stockholm, Sweden).
5: * Copyright (c) 2004 - 2019 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 "curl_setup.h"
36:
37: #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
38:
39: #ifdef HAVE_NETDB_H
40: #include <netdb.h>
41: #endif
42:
43: #include "urldata.h"
44: #include "curl_base64.h"
45: #include "ftp.h"
46: #include "curl_gssapi.h"
47: #include "sendf.h"
48: #include "curl_sec.h"
49: #include "warnless.h"
50:
51: /* The last 3 #include files should be in this order */
52: #include "curl_printf.h"
53: #include "curl_memory.h"
54: #include "memdebug.h"
55:
56: static int
57: krb5_init(void *app_data)
58: {
59: gss_ctx_id_t *context = app_data;
60: /* Make sure our context is initialized for krb5_end. */
61: *context = GSS_C_NO_CONTEXT;
62: return 0;
63: }
64:
65: static int
66: krb5_check_prot(void *app_data, int level)
67: {
68: (void)app_data; /* unused */
69: if(level == PROT_CONFIDENTIAL)
70: return -1;
71: return 0;
72: }
73:
74: static int
75: krb5_decode(void *app_data, void *buf, int len,
76: int level UNUSED_PARAM,
77: struct connectdata *conn UNUSED_PARAM)
78: {
79: gss_ctx_id_t *context = app_data;
80: OM_uint32 maj, min;
81: gss_buffer_desc enc, dec;
82:
83: (void)level;
84: (void)conn;
85:
86: enc.value = buf;
87: enc.length = len;
88: maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL);
89: if(maj != GSS_S_COMPLETE) {
90: if(len >= 4)
91: strcpy(buf, "599 ");
92: return -1;
93: }
94:
95: memcpy(buf, dec.value, dec.length);
96: len = curlx_uztosi(dec.length);
97: gss_release_buffer(&min, &dec);
98:
99: return len;
100: }
101:
102: static int
103: krb5_overhead(void *app_data, int level, int len)
104: {
105: /* no arguments are used */
106: (void)app_data;
107: (void)level;
108: (void)len;
109: return 0;
110: }
111:
112: static int
113: krb5_encode(void *app_data, const void *from, int length, int level, void **to)
114: {
115: gss_ctx_id_t *context = app_data;
116: gss_buffer_desc dec, enc;
117: OM_uint32 maj, min;
118: int state;
119: int len;
120:
121: /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
122: * libraries modify the input buffer in gss_wrap()
123: */
124: dec.value = (void *)from;
125: dec.length = length;
126: maj = gss_wrap(&min, *context,
127: level == PROT_PRIVATE,
128: GSS_C_QOP_DEFAULT,
129: &dec, &state, &enc);
130:
131: if(maj != GSS_S_COMPLETE)
132: return -1;
133:
134: /* malloc a new buffer, in case gss_release_buffer doesn't work as
135: expected */
136: *to = malloc(enc.length);
137: if(!*to)
138: return -1;
139: memcpy(*to, enc.value, enc.length);
140: len = curlx_uztosi(enc.length);
141: gss_release_buffer(&min, &enc);
142: return len;
143: }
144:
145: static int
146: krb5_auth(void *app_data, struct connectdata *conn)
147: {
148: int ret = AUTH_OK;
149: char *p;
150: const char *host = conn->host.name;
151: ssize_t nread;
152: curl_socklen_t l = sizeof(conn->local_addr);
153: struct Curl_easy *data = conn->data;
154: CURLcode result;
155: const char *service = data->set.str[STRING_SERVICE_NAME] ?
156: data->set.str[STRING_SERVICE_NAME] :
157: "ftp";
158: const char *srv_host = "host";
159: gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
160: OM_uint32 maj, min;
161: gss_name_t gssname;
162: gss_ctx_id_t *context = app_data;
163: struct gss_channel_bindings_struct chan;
164: size_t base64_sz = 0;
165: struct sockaddr_in **remote_addr =
166: (struct sockaddr_in **)&conn->ip_addr->ai_addr;
167: char *stringp;
168:
169: if(getsockname(conn->sock[FIRSTSOCKET],
170: (struct sockaddr *)&conn->local_addr, &l) < 0)
171: perror("getsockname()");
172:
173: chan.initiator_addrtype = GSS_C_AF_INET;
174: chan.initiator_address.length = l - 4;
175: chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
176: chan.acceptor_addrtype = GSS_C_AF_INET;
177: chan.acceptor_address.length = l - 4;
178: chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
179: chan.application_data.length = 0;
180: chan.application_data.value = NULL;
181:
182: /* this loop will execute twice (once for service, once for host) */
183: for(;;) {
184: /* this really shouldn't be repeated here, but can't help it */
185: if(service == srv_host) {
186: result = Curl_ftpsend(conn, "AUTH GSSAPI");
187: if(result)
188: return -2;
189:
190: if(Curl_GetFTPResponse(&nread, conn, NULL))
191: return -1;
192:
193: if(data->state.buffer[0] != '3')
194: return -1;
195: }
196:
197: stringp = aprintf("%s@%s", service, host);
198: if(!stringp)
199: return -2;
200:
201: input_buffer.value = stringp;
202: input_buffer.length = strlen(stringp);
203: maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
204: &gssname);
205: free(stringp);
206: if(maj != GSS_S_COMPLETE) {
207: gss_release_name(&min, &gssname);
208: if(service == srv_host) {
209: failf(data, "Error importing service name %s@%s", service, host);
210: return AUTH_ERROR;
211: }
212: service = srv_host;
213: continue;
214: }
215: /* We pass NULL as |output_name_type| to avoid a leak. */
216: gss_display_name(&min, gssname, &output_buffer, NULL);
217: Curl_infof(data, "Trying against %s\n", output_buffer.value);
218: gssresp = GSS_C_NO_BUFFER;
219: *context = GSS_C_NO_CONTEXT;
220:
221: do {
222: /* Release the buffer at each iteration to avoid leaking: the first time
223: we are releasing the memory from gss_display_name. The last item is
224: taken care by a final gss_release_buffer. */
225: gss_release_buffer(&min, &output_buffer);
226: ret = AUTH_OK;
227: maj = Curl_gss_init_sec_context(data,
228: &min,
229: context,
230: gssname,
231: &Curl_krb5_mech_oid,
232: &chan,
233: gssresp,
234: &output_buffer,
235: TRUE,
236: NULL);
237:
238: if(gssresp) {
239: free(_gssresp.value);
240: gssresp = NULL;
241: }
242:
243: if(GSS_ERROR(maj)) {
244: Curl_infof(data, "Error creating security context\n");
245: ret = AUTH_ERROR;
246: break;
247: }
248:
249: if(output_buffer.length != 0) {
250: char *cmd;
251:
252: result = Curl_base64_encode(data, (char *)output_buffer.value,
253: output_buffer.length, &p, &base64_sz);
254: if(result) {
255: Curl_infof(data, "base64-encoding: %s\n",
256: curl_easy_strerror(result));
257: ret = AUTH_ERROR;
258: break;
259: }
260:
261: cmd = aprintf("ADAT %s", p);
262: if(cmd)
263: result = Curl_ftpsend(conn, cmd);
264: else
265: result = CURLE_OUT_OF_MEMORY;
266:
267: free(p);
268: free(cmd);
269:
270: if(result) {
271: ret = -2;
272: break;
273: }
274:
275: if(Curl_GetFTPResponse(&nread, conn, NULL)) {
276: ret = -1;
277: break;
278: }
279:
280: if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
281: Curl_infof(data, "Server didn't accept auth data\n");
282: ret = AUTH_ERROR;
283: break;
284: }
285:
286: _gssresp.value = NULL; /* make sure it is initialized */
287: p = data->state.buffer + 4;
288: p = strstr(p, "ADAT=");
289: if(p) {
290: result = Curl_base64_decode(p + 5,
291: (unsigned char **)&_gssresp.value,
292: &_gssresp.length);
293: if(result) {
294: failf(data, "base64-decoding: %s", curl_easy_strerror(result));
295: ret = AUTH_CONTINUE;
296: break;
297: }
298: }
299:
300: gssresp = &_gssresp;
301: }
302: } while(maj == GSS_S_CONTINUE_NEEDED);
303:
304: gss_release_name(&min, &gssname);
305: gss_release_buffer(&min, &output_buffer);
306:
307: if(gssresp)
308: free(_gssresp.value);
309:
310: if(ret == AUTH_OK || service == srv_host)
311: return ret;
312:
313: service = srv_host;
314: }
315: return ret;
316: }
317:
318: static void krb5_end(void *app_data)
319: {
320: OM_uint32 min;
321: gss_ctx_id_t *context = app_data;
322: if(*context != GSS_C_NO_CONTEXT) {
323: OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
324: (void)maj;
325: DEBUGASSERT(maj == GSS_S_COMPLETE);
326: }
327: }
328:
329: struct Curl_sec_client_mech Curl_krb5_client_mech = {
330: "GSSAPI",
331: sizeof(gss_ctx_id_t),
332: krb5_init,
333: krb5_auth,
334: krb5_end,
335: krb5_check_prot,
336: krb5_overhead,
337: krb5_encode,
338: krb5_decode
339: };
340:
341: #endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>