Annotation of embedaddon/curl/lib/socks_gssapi.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
9: * Copyright (C) 2009, Markus Moeller, <markus_moeller@compuserve.com>
10: *
11: * This software is licensed as described in the file COPYING, which
12: * you should have received as part of this distribution. The terms
13: * are also available at https://curl.haxx.se/docs/copyright.html.
14: *
15: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16: * copies of the Software, and permit persons to whom the Software is
17: * furnished to do so, under the terms of the COPYING file.
18: *
19: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20: * KIND, either express or implied.
21: *
22: ***************************************************************************/
23:
24: #include "curl_setup.h"
25:
26: #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY)
27:
28: #include "curl_gssapi.h"
29: #include "urldata.h"
30: #include "sendf.h"
31: #include "connect.h"
32: #include "timeval.h"
33: #include "socks.h"
34: #include "warnless.h"
35:
36: /* The last 3 #include files should be in this order */
37: #include "curl_printf.h"
38: #include "curl_memory.h"
39: #include "memdebug.h"
40:
41: static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
42:
43: /*
44: * Helper GSS-API error functions.
45: */
46: static int check_gss_err(struct Curl_easy *data,
47: OM_uint32 major_status,
48: OM_uint32 minor_status,
49: const char *function)
50: {
51: if(GSS_ERROR(major_status)) {
52: OM_uint32 maj_stat, min_stat;
53: OM_uint32 msg_ctx = 0;
54: gss_buffer_desc status_string;
55: char buf[1024];
56: size_t len;
57:
58: len = 0;
59: msg_ctx = 0;
60: while(!msg_ctx) {
61: /* convert major status code (GSS-API error) to text */
62: maj_stat = gss_display_status(&min_stat, major_status,
63: GSS_C_GSS_CODE,
64: GSS_C_NULL_OID,
65: &msg_ctx, &status_string);
66: if(maj_stat == GSS_S_COMPLETE) {
67: if(sizeof(buf) > len + status_string.length + 1) {
68: strcpy(buf + len, (char *) status_string.value);
69: len += status_string.length;
70: }
71: gss_release_buffer(&min_stat, &status_string);
72: break;
73: }
74: gss_release_buffer(&min_stat, &status_string);
75: }
76: if(sizeof(buf) > len + 3) {
77: strcpy(buf + len, ".\n");
78: len += 2;
79: }
80: msg_ctx = 0;
81: while(!msg_ctx) {
82: /* convert minor status code (underlying routine error) to text */
83: maj_stat = gss_display_status(&min_stat, minor_status,
84: GSS_C_MECH_CODE,
85: GSS_C_NULL_OID,
86: &msg_ctx, &status_string);
87: if(maj_stat == GSS_S_COMPLETE) {
88: if(sizeof(buf) > len + status_string.length)
89: strcpy(buf + len, (char *) status_string.value);
90: gss_release_buffer(&min_stat, &status_string);
91: break;
92: }
93: gss_release_buffer(&min_stat, &status_string);
94: }
95: failf(data, "GSS-API error: %s failed:\n%s", function, buf);
96: return 1;
97: }
98:
99: return 0;
100: }
101:
102: CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
103: struct connectdata *conn)
104: {
105: struct Curl_easy *data = conn->data;
106: curl_socket_t sock = conn->sock[sockindex];
107: CURLcode code;
108: ssize_t actualread;
109: ssize_t written;
110: int result;
111: OM_uint32 gss_major_status, gss_minor_status, gss_status;
112: OM_uint32 gss_ret_flags;
113: int gss_conf_state, gss_enc;
114: gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
115: gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER;
116: gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER;
117: gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER;
118: gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
119: gss_name_t server = GSS_C_NO_NAME;
120: gss_name_t gss_client_name = GSS_C_NO_NAME;
121: unsigned short us_length;
122: char *user = NULL;
123: unsigned char socksreq[4]; /* room for GSS-API exchange header only */
124: const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ?
125: data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd";
126: const size_t serviceptr_length = strlen(serviceptr);
127:
128: /* GSS-API request looks like
129: * +----+------+-----+----------------+
130: * |VER | MTYP | LEN | TOKEN |
131: * +----+------+----------------------+
132: * | 1 | 1 | 2 | up to 2^16 - 1 |
133: * +----+------+-----+----------------+
134: */
135:
136: /* prepare service name */
137: if(strchr(serviceptr, '/')) {
138: service.length = serviceptr_length;
139: service.value = malloc(service.length);
140: if(!service.value)
141: return CURLE_OUT_OF_MEMORY;
142: memcpy(service.value, serviceptr, service.length);
143:
144: gss_major_status = gss_import_name(&gss_minor_status, &service,
145: (gss_OID) GSS_C_NULL_OID, &server);
146: }
147: else {
148: service.value = malloc(serviceptr_length +
149: strlen(conn->socks_proxy.host.name) + 2);
150: if(!service.value)
151: return CURLE_OUT_OF_MEMORY;
152: service.length = serviceptr_length +
153: strlen(conn->socks_proxy.host.name) + 1;
154: msnprintf(service.value, service.length + 1, "%s@%s",
155: serviceptr, conn->socks_proxy.host.name);
156:
157: gss_major_status = gss_import_name(&gss_minor_status, &service,
158: GSS_C_NT_HOSTBASED_SERVICE, &server);
159: }
160:
161: gss_release_buffer(&gss_status, &service); /* clear allocated memory */
162:
163: if(check_gss_err(data, gss_major_status,
164: gss_minor_status, "gss_import_name()")) {
165: failf(data, "Failed to create service name.");
166: gss_release_name(&gss_status, &server);
167: return CURLE_COULDNT_CONNECT;
168: }
169:
170: (void)curlx_nonblock(sock, FALSE);
171:
172: /* As long as we need to keep sending some context info, and there's no */
173: /* errors, keep sending it... */
174: for(;;) {
175: gss_major_status = Curl_gss_init_sec_context(data,
176: &gss_minor_status,
177: &gss_context,
178: server,
179: &Curl_krb5_mech_oid,
180: NULL,
181: gss_token,
182: &gss_send_token,
183: TRUE,
184: &gss_ret_flags);
185:
186: if(gss_token != GSS_C_NO_BUFFER)
187: gss_release_buffer(&gss_status, &gss_recv_token);
188: if(check_gss_err(data, gss_major_status,
189: gss_minor_status, "gss_init_sec_context")) {
190: gss_release_name(&gss_status, &server);
191: gss_release_buffer(&gss_status, &gss_recv_token);
192: gss_release_buffer(&gss_status, &gss_send_token);
193: gss_delete_sec_context(&gss_status, &gss_context, NULL);
194: failf(data, "Failed to initial GSS-API token.");
195: return CURLE_COULDNT_CONNECT;
196: }
197:
198: if(gss_send_token.length != 0) {
199: socksreq[0] = 1; /* GSS-API subnegotiation version */
200: socksreq[1] = 1; /* authentication message type */
201: us_length = htons((short)gss_send_token.length);
202: memcpy(socksreq + 2, &us_length, sizeof(short));
203:
204: code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
205: if(code || (4 != written)) {
206: failf(data, "Failed to send GSS-API authentication request.");
207: gss_release_name(&gss_status, &server);
208: gss_release_buffer(&gss_status, &gss_recv_token);
209: gss_release_buffer(&gss_status, &gss_send_token);
210: gss_delete_sec_context(&gss_status, &gss_context, NULL);
211: return CURLE_COULDNT_CONNECT;
212: }
213:
214: code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
215: gss_send_token.length, &written);
216:
217: if(code || ((ssize_t)gss_send_token.length != written)) {
218: failf(data, "Failed to send GSS-API authentication token.");
219: gss_release_name(&gss_status, &server);
220: gss_release_buffer(&gss_status, &gss_recv_token);
221: gss_release_buffer(&gss_status, &gss_send_token);
222: gss_delete_sec_context(&gss_status, &gss_context, NULL);
223: return CURLE_COULDNT_CONNECT;
224: }
225:
226: }
227:
228: gss_release_buffer(&gss_status, &gss_send_token);
229: gss_release_buffer(&gss_status, &gss_recv_token);
230: if(gss_major_status != GSS_S_CONTINUE_NEEDED)
231: break;
232:
233: /* analyse response */
234:
235: /* GSS-API response looks like
236: * +----+------+-----+----------------+
237: * |VER | MTYP | LEN | TOKEN |
238: * +----+------+----------------------+
239: * | 1 | 1 | 2 | up to 2^16 - 1 |
240: * +----+------+-----+----------------+
241: */
242:
243: result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
244: if(result || (actualread != 4)) {
245: failf(data, "Failed to receive GSS-API authentication response.");
246: gss_release_name(&gss_status, &server);
247: gss_delete_sec_context(&gss_status, &gss_context, NULL);
248: return CURLE_COULDNT_CONNECT;
249: }
250:
251: /* ignore the first (VER) byte */
252: if(socksreq[1] == 255) { /* status / message type */
253: failf(data, "User was rejected by the SOCKS5 server (%d %d).",
254: socksreq[0], socksreq[1]);
255: gss_release_name(&gss_status, &server);
256: gss_delete_sec_context(&gss_status, &gss_context, NULL);
257: return CURLE_COULDNT_CONNECT;
258: }
259:
260: if(socksreq[1] != 1) { /* status / messgae type */
261: failf(data, "Invalid GSS-API authentication response type (%d %d).",
262: socksreq[0], socksreq[1]);
263: gss_release_name(&gss_status, &server);
264: gss_delete_sec_context(&gss_status, &gss_context, NULL);
265: return CURLE_COULDNT_CONNECT;
266: }
267:
268: memcpy(&us_length, socksreq + 2, sizeof(short));
269: us_length = ntohs(us_length);
270:
271: gss_recv_token.length = us_length;
272: gss_recv_token.value = malloc(us_length);
273: if(!gss_recv_token.value) {
274: failf(data,
275: "Could not allocate memory for GSS-API authentication "
276: "response token.");
277: gss_release_name(&gss_status, &server);
278: gss_delete_sec_context(&gss_status, &gss_context, NULL);
279: return CURLE_OUT_OF_MEMORY;
280: }
281:
282: result = Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
283: gss_recv_token.length, &actualread);
284:
285: if(result || (actualread != us_length)) {
286: failf(data, "Failed to receive GSS-API authentication token.");
287: gss_release_name(&gss_status, &server);
288: gss_release_buffer(&gss_status, &gss_recv_token);
289: gss_delete_sec_context(&gss_status, &gss_context, NULL);
290: return CURLE_COULDNT_CONNECT;
291: }
292:
293: gss_token = &gss_recv_token;
294: }
295:
296: gss_release_name(&gss_status, &server);
297:
298: /* Everything is good so far, user was authenticated! */
299: gss_major_status = gss_inquire_context(&gss_minor_status, gss_context,
300: &gss_client_name, NULL, NULL, NULL,
301: NULL, NULL, NULL);
302: if(check_gss_err(data, gss_major_status,
303: gss_minor_status, "gss_inquire_context")) {
304: gss_delete_sec_context(&gss_status, &gss_context, NULL);
305: gss_release_name(&gss_status, &gss_client_name);
306: failf(data, "Failed to determine user name.");
307: return CURLE_COULDNT_CONNECT;
308: }
309: gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
310: &gss_send_token, NULL);
311: if(check_gss_err(data, gss_major_status,
312: gss_minor_status, "gss_display_name")) {
313: gss_delete_sec_context(&gss_status, &gss_context, NULL);
314: gss_release_name(&gss_status, &gss_client_name);
315: gss_release_buffer(&gss_status, &gss_send_token);
316: failf(data, "Failed to determine user name.");
317: return CURLE_COULDNT_CONNECT;
318: }
319: user = malloc(gss_send_token.length + 1);
320: if(!user) {
321: gss_delete_sec_context(&gss_status, &gss_context, NULL);
322: gss_release_name(&gss_status, &gss_client_name);
323: gss_release_buffer(&gss_status, &gss_send_token);
324: return CURLE_OUT_OF_MEMORY;
325: }
326:
327: memcpy(user, gss_send_token.value, gss_send_token.length);
328: user[gss_send_token.length] = '\0';
329: gss_release_name(&gss_status, &gss_client_name);
330: gss_release_buffer(&gss_status, &gss_send_token);
331: infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",user);
332: free(user);
333: user = NULL;
334:
335: /* Do encryption */
336: socksreq[0] = 1; /* GSS-API subnegotiation version */
337: socksreq[1] = 2; /* encryption message type */
338:
339: gss_enc = 0; /* no data protection */
340: /* do confidentiality protection if supported */
341: if(gss_ret_flags & GSS_C_CONF_FLAG)
342: gss_enc = 2;
343: /* else do integrity protection */
344: else if(gss_ret_flags & GSS_C_INTEG_FLAG)
345: gss_enc = 1;
346:
347: infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
348: (gss_enc == 0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
349: /* force for the moment to no data protection */
350: gss_enc = 0;
351: /*
352: * Sending the encryption type in clear seems wrong. It should be
353: * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
354: * The NEC reference implementations on which this is based is
355: * therefore at fault
356: *
357: * +------+------+------+.......................+
358: * + ver | mtyp | len | token |
359: * +------+------+------+.......................+
360: * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
361: * +------+------+------+.......................+
362: *
363: * Where:
364: *
365: * - "ver" is the protocol version number, here 1 to represent the
366: * first version of the SOCKS/GSS-API protocol
367: *
368: * - "mtyp" is the message type, here 2 to represent a protection
369: * -level negotiation message
370: *
371: * - "len" is the length of the "token" field in octets
372: *
373: * - "token" is the GSS-API encapsulated protection level
374: *
375: * The token is produced by encapsulating an octet containing the
376: * required protection level using gss_seal()/gss_wrap() with conf_req
377: * set to FALSE. The token is verified using gss_unseal()/
378: * gss_unwrap().
379: *
380: */
381: if(data->set.socks5_gssapi_nec) {
382: us_length = htons((short)1);
383: memcpy(socksreq + 2, &us_length, sizeof(short));
384: }
385: else {
386: gss_send_token.length = 1;
387: gss_send_token.value = malloc(1);
388: if(!gss_send_token.value) {
389: gss_delete_sec_context(&gss_status, &gss_context, NULL);
390: return CURLE_OUT_OF_MEMORY;
391: }
392: memcpy(gss_send_token.value, &gss_enc, 1);
393:
394: gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
395: GSS_C_QOP_DEFAULT, &gss_send_token,
396: &gss_conf_state, &gss_w_token);
397:
398: if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) {
399: gss_release_buffer(&gss_status, &gss_send_token);
400: gss_release_buffer(&gss_status, &gss_w_token);
401: gss_delete_sec_context(&gss_status, &gss_context, NULL);
402: failf(data, "Failed to wrap GSS-API encryption value into token.");
403: return CURLE_COULDNT_CONNECT;
404: }
405: gss_release_buffer(&gss_status, &gss_send_token);
406:
407: us_length = htons((short)gss_w_token.length);
408: memcpy(socksreq + 2, &us_length, sizeof(short));
409: }
410:
411: code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
412: if(code || (4 != written)) {
413: failf(data, "Failed to send GSS-API encryption request.");
414: gss_release_buffer(&gss_status, &gss_w_token);
415: gss_delete_sec_context(&gss_status, &gss_context, NULL);
416: return CURLE_COULDNT_CONNECT;
417: }
418:
419: if(data->set.socks5_gssapi_nec) {
420: memcpy(socksreq, &gss_enc, 1);
421: code = Curl_write_plain(conn, sock, socksreq, 1, &written);
422: if(code || ( 1 != written)) {
423: failf(data, "Failed to send GSS-API encryption type.");
424: gss_delete_sec_context(&gss_status, &gss_context, NULL);
425: return CURLE_COULDNT_CONNECT;
426: }
427: }
428: else {
429: code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
430: gss_w_token.length, &written);
431: if(code || ((ssize_t)gss_w_token.length != written)) {
432: failf(data, "Failed to send GSS-API encryption type.");
433: gss_release_buffer(&gss_status, &gss_w_token);
434: gss_delete_sec_context(&gss_status, &gss_context, NULL);
435: return CURLE_COULDNT_CONNECT;
436: }
437: gss_release_buffer(&gss_status, &gss_w_token);
438: }
439:
440: result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
441: if(result || (actualread != 4)) {
442: failf(data, "Failed to receive GSS-API encryption response.");
443: gss_delete_sec_context(&gss_status, &gss_context, NULL);
444: return CURLE_COULDNT_CONNECT;
445: }
446:
447: /* ignore the first (VER) byte */
448: if(socksreq[1] == 255) { /* status / message type */
449: failf(data, "User was rejected by the SOCKS5 server (%d %d).",
450: socksreq[0], socksreq[1]);
451: gss_delete_sec_context(&gss_status, &gss_context, NULL);
452: return CURLE_COULDNT_CONNECT;
453: }
454:
455: if(socksreq[1] != 2) { /* status / messgae type */
456: failf(data, "Invalid GSS-API encryption response type (%d %d).",
457: socksreq[0], socksreq[1]);
458: gss_delete_sec_context(&gss_status, &gss_context, NULL);
459: return CURLE_COULDNT_CONNECT;
460: }
461:
462: memcpy(&us_length, socksreq + 2, sizeof(short));
463: us_length = ntohs(us_length);
464:
465: gss_recv_token.length = us_length;
466: gss_recv_token.value = malloc(gss_recv_token.length);
467: if(!gss_recv_token.value) {
468: gss_delete_sec_context(&gss_status, &gss_context, NULL);
469: return CURLE_OUT_OF_MEMORY;
470: }
471: result = Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
472: gss_recv_token.length, &actualread);
473:
474: if(result || (actualread != us_length)) {
475: failf(data, "Failed to receive GSS-API encryptrion type.");
476: gss_release_buffer(&gss_status, &gss_recv_token);
477: gss_delete_sec_context(&gss_status, &gss_context, NULL);
478: return CURLE_COULDNT_CONNECT;
479: }
480:
481: if(!data->set.socks5_gssapi_nec) {
482: gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
483: &gss_recv_token, &gss_w_token,
484: 0, GSS_C_QOP_DEFAULT);
485:
486: if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) {
487: gss_release_buffer(&gss_status, &gss_recv_token);
488: gss_release_buffer(&gss_status, &gss_w_token);
489: gss_delete_sec_context(&gss_status, &gss_context, NULL);
490: failf(data, "Failed to unwrap GSS-API encryption value into token.");
491: return CURLE_COULDNT_CONNECT;
492: }
493: gss_release_buffer(&gss_status, &gss_recv_token);
494:
495: if(gss_w_token.length != 1) {
496: failf(data, "Invalid GSS-API encryption response length (%d).",
497: gss_w_token.length);
498: gss_release_buffer(&gss_status, &gss_w_token);
499: gss_delete_sec_context(&gss_status, &gss_context, NULL);
500: return CURLE_COULDNT_CONNECT;
501: }
502:
503: memcpy(socksreq, gss_w_token.value, gss_w_token.length);
504: gss_release_buffer(&gss_status, &gss_w_token);
505: }
506: else {
507: if(gss_recv_token.length != 1) {
508: failf(data, "Invalid GSS-API encryption response length (%d).",
509: gss_recv_token.length);
510: gss_release_buffer(&gss_status, &gss_recv_token);
511: gss_delete_sec_context(&gss_status, &gss_context, NULL);
512: return CURLE_COULDNT_CONNECT;
513: }
514:
515: memcpy(socksreq, gss_recv_token.value, gss_recv_token.length);
516: gss_release_buffer(&gss_status, &gss_recv_token);
517: }
518:
519: (void)curlx_nonblock(sock, TRUE);
520:
521: infof(data, "SOCKS5 access with%s protection granted.\n",
522: (socksreq[0] == 0)?"out GSS-API data":
523: ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
524:
525: conn->socks5_gssapi_enctype = socksreq[0];
526: if(socksreq[0] == 0)
527: gss_delete_sec_context(&gss_status, &gss_context, NULL);
528:
529: return CURLE_OK;
530: }
531:
532: #endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>