1 /*
2 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2019
3 * Inferno Nettverk A/S, Norway. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. The above copyright notice, this list of conditions and the following
9 * disclaimer must appear in all copies of the software, derivative works
10 * or modified versions, and any portions thereof, aswell as in all
11 * supporting documentation.
12 * 2. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by
15 * Inferno Nettverk A/S, Norway.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Inferno Nettverk A/S requests users of this software to return to
31 *
32 * Software Distribution Coordinator or sdc@inet.no
33 * Inferno Nettverk A/S
34 * Oslo Research Park
35 * Gaustadall�en 21
36 * NO-0349 Oslo
37 * Norway
38 *
39 * any improvements or extensions that they make and grant Inferno Nettverk A/S
40 * the rights to redistribute these changes.
41 *
42 */
43
44 /*
45 * This code was contributed by
46 * Markus Moeller (markus_moeller at compuserve.com).
47 */
48
49 #include "common.h"
50
51 #if HAVE_GSSAPI
52
53 static const char rcsid[] =
54 "$Id: method_gssapi.c,v 1.73.4.1.6.4 2020/11/11 17:02:28 karls Exp $";
55
56 static negotiate_result_t
57 recv_gssapi_auth_ver(int s, request_t *request, negotiate_state_t *state);
58
59 static negotiate_result_t
60 recv_gssapi_auth_type(int s, request_t *request, negotiate_state_t *state);
61
62 static negotiate_result_t
63 recv_gssapi_auth_len(int s, request_t *request, negotiate_state_t *state);
64
65 static negotiate_result_t
66 recv_gssapi_auth_token(int s, request_t *request, negotiate_state_t *state);
67
68 static negotiate_result_t
69 recv_gssapi_enc_ver(int s, request_t *request, negotiate_state_t *state);
70
71 static negotiate_result_t
72 recv_gssapi_enc_type(int s, request_t *request, negotiate_state_t *state);
73
74 static negotiate_result_t
75 recv_gssapi_enc_len(int s, request_t *request, negotiate_state_t *state);
76
77 static negotiate_result_t
78 recv_gssapi_enc_token(int s, request_t *request, negotiate_state_t *state);
79
80 static negotiate_result_t
81 recv_gssapi_packet(int s, request_t *request, negotiate_state_t *state);
82
83 static negotiate_result_t
84 recv_gssapi_packet_ver(int s, request_t *request, negotiate_state_t *state);
85
86 static negotiate_result_t
87 recv_gssapi_packet_type(int s, request_t *request, negotiate_state_t *state);
88
89 static negotiate_result_t
90 recv_gssapi_packet_len(int s, request_t *request, negotiate_state_t *state);
91
92 static negotiate_result_t
93 recv_gssapi_packet_token(int s, request_t *request, negotiate_state_t *state);
94
95
96 negotiate_result_t
method_gssapi(s,request,state)97 method_gssapi(s, request, state)
98 int s;
99 request_t *request;
100 negotiate_state_t *state;
101
102 {
103 const char *function = "method_gssapi()";
104
105 slog(LOG_DEBUG, "%s", function);
106
107 request->auth->mdata.gssapi.state.id = GSS_C_NO_CONTEXT;
108
109 state->rcurrent = recv_gssapi_auth_ver;
110 return state->rcurrent(s, request, state);
111 }
112
113 /*
114 * RFC1961: client request
115 *
116 * +------+------+------+.......................+
117 * + ver | mtyp | len | token |
118 * +------+------+------+.......................+
119 * + 0x01 | 0x01 | 0x02 | up to 2^16 - 1 octets |
120 * +------+------+------+.......................+
121 *
122 */
123
124 static negotiate_result_t
recv_gssapi_auth_ver(s,request,state)125 recv_gssapi_auth_ver(s, request, state)
126 int s;
127 request_t *request;
128 negotiate_state_t *state;
129 {
130 const char *function = "recv_gssapi_auth_ver()";
131 unsigned char gssapi_auth_version;
132
133 INIT(sizeof(gssapi_auth_version));
134 CHECK(&gssapi_auth_version, request->auth, NULL);
135
136 switch (gssapi_auth_version) {
137 case SOCKS_GSSAPI_VERSION:
138 break;
139
140 default:
141 snprintf(state->emsg, sizeof(state->emsg),
142 "%s: unknown %s version on gssapi packet from client: %d",
143 function,
144 proxyprotocol2string(request->version),
145 gssapi_auth_version);
146
147 return NEGOTIATE_ERROR;
148 }
149
150 state->rcurrent = recv_gssapi_auth_type;
151 return state->rcurrent(s, request, state);
152 }
153
154 static negotiate_result_t
recv_gssapi_auth_type(s,request,state)155 recv_gssapi_auth_type(s, request, state)
156 int s;
157 request_t *request;
158 negotiate_state_t *state;
159 {
160 const char *function = "recv_gssapi_auth_type()";
161 unsigned char gssapi_auth_type;
162
163 INIT(sizeof(gssapi_auth_type));
164 CHECK(&gssapi_auth_type, request->auth, NULL);
165
166 switch (gssapi_auth_type) {
167 case SOCKS_GSSAPI_AUTHENTICATION:
168 break;
169
170 case SOCKS_GSSAPI_ENCRYPTION:
171 case SOCKS_GSSAPI_PACKET:
172 snprintf(state->emsg, sizeof(state->emsg),
173 "%s: received out of sequence exchange from client. "
174 "Got type %d, expected type %d",
175 function, gssapi_auth_type, SOCKS_GSSAPI_AUTHENTICATION);
176
177 return NEGOTIATE_ERROR;
178
179 default:
180 snprintf(state->emsg, sizeof(state->emsg),
181 "%s: unknown type on gssapi packet from client: %d",
182 function, gssapi_auth_type);
183
184 return NEGOTIATE_ERROR;
185 }
186
187 state->rcurrent = recv_gssapi_auth_len;
188 return state->rcurrent(s, request, state);
189 }
190
191 static negotiate_result_t
recv_gssapi_auth_len(s,request,state)192 recv_gssapi_auth_len(s, request, state)
193 int s;
194 request_t *request;
195 negotiate_state_t *state;
196 {
197 INIT(sizeof(state->gssapitoken_len));
198 CHECK(&state->gssapitoken_len, request->auth, NULL);
199
200 state->gssapitoken_len
201 = ntohs((short)state->gssapitoken_len);
202
203 state->rcurrent = recv_gssapi_auth_token;
204 return state->rcurrent(s, request, state);
205 }
206
207 static negotiate_result_t
recv_gssapi_auth_token(s,request,state)208 recv_gssapi_auth_token(s, request, state)
209 int s;
210 request_t *request;
211 negotiate_state_t *state;
212 {
213 const char *function = "recv_gssapi_auth_token()";
214 sendto_info_t sendtoflags;
215 gss_name_t client_name = GSS_C_NO_NAME;
216 gss_name_t server_name = GSS_C_NO_NAME;
217 gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
218 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
219 gss_buffer_desc input_token;
220 OM_uint32 ret_flags, major_status, minor_status;
221 unsigned short token_length;
222 ssize_t rc;
223 size_t buflen;
224 unsigned char buf[GSSAPI_HLEN + MAXGSSAPITOKENLEN];
225 char env[sizeof(request->auth->mdata.gssapi.keytab)], emsg[1024];
226 #if HAVE_PAC
227 krb5_context k5_context = NULL;
228 krb5_error_code ret;
229 krb5_pac pac;
230 #if HAVE_HEIMDAL_KERBEROS
231 gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
232 #else
233 gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
234 #endif /* HAVE_HEIMDAL_KERBEROS */
235 #endif /* HAVE_PAC */
236
237 INIT(state->gssapitoken_len);
238
239 input_token.length = state->gssapitoken_len;
240 input_token.value = buf;
241 SASSERTX(input_token.length <= sizeof(buf));
242
243 CHECK(input_token.value, request->auth, NULL);
244
245 STRCPY_ASSERTLEN(env, request->auth->mdata.gssapi.keytab);
246 setenv("KRB5_KTNAME", env, 1);
247
248 #if HAVE_HEIMDAL_KERBEROS
249 gsskrb5_register_acceptor_identity(request->auth->mdata.gssapi.keytab);
250 #endif /* HAVE_HEIMDAL_KERBEROS */
251
252 slog(LOG_DEBUG,"%s: using gssapi service name %s",
253 function, request->auth->mdata.gssapi.servicename);
254
255 if (strcasecmp(request->auth->mdata.gssapi.servicename, "GSS_C_NO_NAME")
256 != 0) {
257 gss_buffer_desc service;
258
259 service.value = request->auth->mdata.gssapi.servicename;
260 service.length = strlen(service.value);
261
262 major_status
263 = gss_import_name(&minor_status,
264 &service,
265 strchr(request->auth->mdata.gssapi.servicename, '/')
266 != NULL ?
267 (gss_OID)GSS_C_NULL_OID
268 : (gss_OID)gss_nt_service_name,
269 &server_name);
270
271 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
272 snprintf(state->emsg, sizeof(state->emsg),
273 "%s: gss_import_name() %s", function, emsg);
274
275 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
276 return NEGOTIATE_ERROR;
277 }
278 }
279
280 sockd_priv(SOCKD_PRIV_GSSAPI, PRIV_ON);
281 major_status = gss_acquire_cred(&minor_status,
282 server_name,
283 GSS_C_INDEFINITE,
284 GSS_C_NO_OID_SET,
285 GSS_C_ACCEPT,
286 &server_creds,
287 NULL,
288 NULL);
289 sockd_priv(SOCKD_PRIV_GSSAPI, PRIV_OFF);
290
291 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
292 snprintf(state->emsg, sizeof(state->emsg), "%s: gss_acquire_cred(): %s",
293 function, emsg);
294
295 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
296 return NEGOTIATE_ERROR;
297 }
298
299 sockd_priv(SOCKD_PRIV_GSSAPI, PRIV_ON);
300 major_status
301 = gss_accept_sec_context(&minor_status,
302 &request->auth->mdata.gssapi.state.id,
303 server_creds,
304 &input_token,
305 GSS_C_NO_CHANNEL_BINDINGS,
306 &client_name,
307 NULL,
308 &output_token,
309 &ret_flags,
310 NULL,
311 NULL);
312 sockd_priv(SOCKD_PRIV_GSSAPI, PRIV_OFF);
313
314 slog(LOG_DEBUG, "%s: length of output_token is %lu",
315 function, (unsigned long)output_token.length);
316
317 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
318 snprintf(state->emsg, sizeof(state->emsg),
319 "%s: gss_accept_sec_context() failed: %s", function, emsg);
320
321 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
322 return NEGOTIATE_ERROR;
323 }
324
325 /*
326 * Don't need the input token anymore, so use the token buffer
327 * to hold the reply from now on.
328 */
329
330 /*
331 * RFC1961: server reply
332 *
333 * +------+------+------+.......................+
334 * + ver | mtyp | len | token |
335 * +------+------+------+.......................+
336 * + 0x01 | 0x01 | 0x02 | up to 2^16 - 1 octets |
337 * +------+------+------+.......................+
338 *
339 */
340
341 SASSERTX(GSSAPI_HLEN + output_token.length <= sizeof(buf));
342
343 buflen = 0;
344 buf[buflen++] = SOCKS_GSSAPI_VERSION;
345 buf[buflen++] = SOCKS_GSSAPI_AUTHENTICATION;
346
347 token_length = htons(output_token.length);
348 memcpy(&buf[buflen], &token_length, sizeof(token_length));
349 buflen += sizeof(token_length);
350
351 memcpy(&buf[buflen], output_token.value, output_token.length);
352 buflen += output_token.length;
353
354 CLEAN_GSS_TOKEN(output_token);
355
356 bzero(&sendtoflags, sizeof(sendtoflags));
357 sendtoflags.side = INTERNALIF;
358
359 if ((rc = socks_sendton(s,
360 buf,
361 buflen,
362 0,
363 0,
364 NULL,
365 0,
366 NULL,
367 request->auth)) != (ssize_t)buflen) {
368 snprintf(state->emsg, sizeof(state->emsg),
369 "socks_sendton() token: wrote %ld out of %lu byte%s: %s",
370 (long)rc,
371 (unsigned long)buflen,
372 buflen == 1 ? "" : "s",
373 strerror(errno));
374
375 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
376 return NEGOTIATE_ERROR;
377 }
378
379 if (major_status == GSS_S_COMPLETE) {
380 /* Get username */
381 major_status = gss_display_name(&minor_status, client_name, &output_token,
382 NULL);
383
384 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
385 snprintf(state->emsg, sizeof(state->emsg),
386 "%s: gss_display_name(): %s", function, emsg);
387
388 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
389 return NEGOTIATE_ERROR;
390 }
391
392 memcpy(request->auth->mdata.gssapi.name, output_token.value,
393 output_token.length);
394 request->auth->mdata.gssapi.name[output_token.length] = NUL;
395
396 #if HAVE_PAC
397 ret = krb5_init_context(&k5_context);
398 if (!krb5_err_isset(k5_context, emsg, sizeof(emsg), ret)) {
399 #define ADWIN2KPAC 128
400 #if HAVE_HEIMDAL_KERBEROS
401 major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status,
402 gss_context,
403 ADWIN2KPAC,
404 &data_set);
405 if (!gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
406 ret = krb5_pac_parse(k5_context, data_set.value, data_set.length, &pac);
407 gss_release_buffer(&minor_status, &data_set);
408 if (!check_k5_err(k5_context, "krb5_pac_parse", ret)) {
409 if (get_sids(request->auth->mdata.gssapi.sids, k5_context, pac, state)<0) {
410 slog(LOG_INFO, "%s: error in getting sids", function);
411 }
412 krb5_pac_free(k5_context, pac);
413 }
414 krb5_free_context(k5context);
415 } else {
416 /* Not a major error - maybe not a MS AD setup */
417 snprintf(state->emsg, sizeof(state->emsg),
418 "%s: gsskrb5_extract_authz_data_from_sec_context(): %s", function, emsg);
419 slog(LOG_INFO, "%s: gsskrb5_extract_authz_data_from_sec_context(): %s", function, emsg);
420 }
421 #else /* !HAVE_HEIMDAL_KERBEROS */
422 type_id.value = (void *)"mspac";
423 type_id.length = strlen((char *)type_id.value);
424 #define KRB5PACLOGONINFO 1
425 major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac);
426 if (!gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))){
427 if (get_sids(request->auth->mdata.gssapi.sids, k5_context, pac, state)<0){
428 slog(LOG_INFO, "%s: error in getting sids", function);
429 }
430 } else {
431 /* Not a major error - maybe not a MS AD setup */
432 snprintf(state->emsg, sizeof(state->emsg),
433 "%s: gss_map_name_to_any(): %s", function, emsg);
434 slog(LOG_INFO, "%s: gss_map_name_to_any(): %s", function, emsg);
435 }
436 (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
437 krb5_free_context(k5_context);
438 #endif /* !HAVE_HEIMDAL_KERBEROS */
439 } else {
440 snprintf(state->emsg, sizeof(state->emsg),
441 "%s: krb5_init_context(): %s", function, emsg);
442 slog(LOG_INFO, "%s: krb5_init_context(): %s", function, emsg);
443 }
444 #endif /* HAVE_PAC */
445
446 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
447 CLEAN_GSS_TOKEN(output_token);
448
449 state->rcurrent = recv_gssapi_enc_ver;
450 return state->rcurrent(s, request, state);
451 }
452 else if (major_status == GSS_S_CONTINUE_NEEDED) {
453 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
454
455 /* expect a new token, with the version, length, etc. header. */
456 state->rcurrent = recv_gssapi_auth_ver;
457
458 /* presumably client is awaiting our response. */
459 return NEGOTIATE_CONTINUE;
460 }
461 else {
462 snprintf(state->emsg, sizeof(state->emsg),
463 "%s: unknown gss major_status %d", function, major_status);
464
465 CLEAN_GSS_AUTH(client_name, server_name, server_creds);
466 return NEGOTIATE_ERROR;
467 }
468
469 /* NOTREACHED */
470 }
471
472 static negotiate_result_t
recv_gssapi_enc_ver(s,request,state)473 recv_gssapi_enc_ver(s, request, state)
474 int s;
475 request_t *request;
476 negotiate_state_t *state;
477 {
478 const char *function = "recv_gssapi_enc_ver()";
479 unsigned char gssapi_enc_version;
480
481 INIT(sizeof(gssapi_enc_version));
482 CHECK(&gssapi_enc_version, request->auth, NULL);
483
484 switch (gssapi_enc_version) {
485 case SOCKS_GSSAPI_VERSION:
486 break;
487
488 default:
489 snprintf(state->emsg, sizeof(state->emsg),
490 "%s: unknown version on gssapi packet from client: %d",
491 function, gssapi_enc_version);
492
493 return NEGOTIATE_ERROR;
494 }
495
496 state->rcurrent = recv_gssapi_enc_type;
497 return state->rcurrent(s, request, state);
498 }
499
500 /*
501 * RFC1961: client request
502 *
503 * +------+------+------+.......................+
504 * + ver | mtyp | len | token |
505 * +------+------+------+.......................+
506 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
507 * +------+------+------+.......................+
508 *
509 */
510 static negotiate_result_t
recv_gssapi_enc_type(s,request,state)511 recv_gssapi_enc_type(s, request, state)
512 int s;
513 request_t *request;
514 negotiate_state_t *state;
515 {
516 const char *function = "recv_gssapi_enc_type()";
517 unsigned char gssapi_enc_type;
518
519 INIT(sizeof(gssapi_enc_type));
520 CHECK(&gssapi_enc_type, request->auth, NULL);
521
522 switch (gssapi_enc_type) {
523 case SOCKS_GSSAPI_INTEGRITY:
524 case SOCKS_GSSAPI_CONFIDENTIALITY:
525 break;
526
527 case SOCKS_GSSAPI_PERMESSAGE:
528 snprintf(state->emsg, sizeof(state->emsg),
529 "%s: unsupported per message encryption on gssapi packet from client",
530 function);
531
532 return NEGOTIATE_ERROR;
533
534 default:
535 snprintf(state->emsg, sizeof(state->emsg),
536 "%s: unknown type on gssapi packet from client: %d",
537 function, gssapi_enc_type);
538
539 return NEGOTIATE_ERROR;
540 }
541
542 state->rcurrent = recv_gssapi_enc_len;
543 return state->rcurrent(s, request, state);
544 }
545
546 static negotiate_result_t
recv_gssapi_enc_len(s,request,state)547 recv_gssapi_enc_len(s, request, state)
548 int s;
549 request_t *request;
550 negotiate_state_t *state;
551 {
552
553 INIT(sizeof(state->gssapitoken_len));
554 CHECK(&state->gssapitoken_len, request->auth, NULL);
555
556 state->gssapitoken_len
557 = ntohs((short)state->gssapitoken_len);
558
559 state->rcurrent = recv_gssapi_enc_token;
560 return state->rcurrent(s, request, state);
561 }
562
563 static negotiate_result_t
recv_gssapi_enc_token(s,request,state)564 recv_gssapi_enc_token(s, request, state)
565 int s;
566 request_t *request;
567 negotiate_state_t *state;
568 {
569 const char *function = "recv_gssapi_enc_token()";
570 OM_uint32 minor_status, major_status;
571 gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
572 unsigned short unsigned_short;
573 ssize_t rc;
574 size_t buflen;
575 unsigned char buf[GSSAPI_HLEN + MAXGSSAPITOKENLEN], gss_enc, gss_server_enc;
576 int conf_state, do_rfc1961;
577 char emsg[1024];
578
579 INIT(state->gssapitoken_len);
580
581 input_token.length = state->gssapitoken_len;
582 input_token.value = buf;
583
584 CHECK(input_token.value, request->auth, NULL);
585
586 if (state->gssapitoken_len == 1 /* clear text encryption selection packet */
587 && request->auth->mdata.gssapi.encryption.nec)
588 do_rfc1961 = 0; /*
589 * It seems the NEC reference implementation does not do
590 * this right. Try to support it if configured as such.
591 */
592 else
593 do_rfc1961 = 1;
594
595 slog(LOG_DEBUG, "%s: rule assumes client uses %s exchange",
596 function, do_rfc1961? "rfc1961 encrypted" : "NEC-style unencrypted");
597
598 if (do_rfc1961) {
599 major_status = gss_unwrap(&minor_status,
600 request->auth->mdata.gssapi.state.id,
601 &input_token,
602 &output_token,
603 0,
604 GSS_C_QOP_DEFAULT);
605
606 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
607 snprintf(state->emsg, sizeof(state->emsg), "%s: gss_unwrap(): %s",
608 function, emsg);
609
610 CLEAN_GSS_TOKEN(output_token);
611 return NEGOTIATE_ERROR;
612 }
613
614 if (output_token.length != 1) {
615 snprintf(state->emsg, sizeof(state->emsg),
616 "%s: gssapi encryption unwrapped length is wrong: expected 1 "
617 "but is %lu. If the client is a NEC-based client you can "
618 "try enabling NEC (NOTE: unencrypted exchange) "
619 "client-compatibility for this client-rule",
620 function, (unsigned long)output_token.length);
621
622 CLEAN_GSS_TOKEN(output_token);
623 return NEGOTIATE_ERROR;
624 }
625
626 SASSERTX(output_token.length == sizeof(gss_enc));
627 memcpy(&gss_enc, output_token.value, output_token.length);
628 CLEAN_GSS_TOKEN(output_token);
629 }
630 else {
631 SASSERTX(input_token.length == sizeof(gss_enc));
632 memcpy(&gss_enc, input_token.value, input_token.length);
633 }
634
635 if ((gss_enc == SOCKS_GSSAPI_CLEAR
636 && !request->auth->mdata.gssapi.encryption.clear)
637 || (gss_enc == SOCKS_GSSAPI_INTEGRITY
638 && !request->auth->mdata.gssapi.encryption.integrity)
639 || (gss_enc == SOCKS_GSSAPI_CONFIDENTIALITY
640 && !request->auth->mdata.gssapi.encryption.confidentiality)
641 || (gss_enc == SOCKS_GSSAPI_PERMESSAGE) ) {
642 /*
643 * enforce server encryption type, regardless of what client offers.
644 */
645
646 snprintf(state->emsg, sizeof(state->emsg),
647 "the client requests different authentication from what we offer. "
648 "Client requests %s (0x%x), but we offer: clear/%d, integrity/%d, "
649 "confidentiality/%d, per message/%d",
650 gssapiprotection2string(gss_enc),
651 gss_enc,
652 request->auth->mdata.gssapi.encryption.clear,
653 request->auth->mdata.gssapi.encryption.integrity,
654 request->auth->mdata.gssapi.encryption.confidentiality,
655 request->auth->mdata.gssapi.encryption.permessage);
656
657 return NEGOTIATE_ERROR;
658 }
659
660 gss_server_enc = gss_enc;
661
662 slog(LOG_DEBUG, "%s: using %s protection (%d)",
663 function, gssapiprotection2string(gss_server_enc), gss_server_enc);
664
665 if (do_rfc1961) {
666 input_token.length = 1;
667 input_token.value = &gss_server_enc;
668
669 major_status = gss_wrap(&minor_status,
670 request->auth->mdata.gssapi.state.id,
671 GSS_REQ_INT,
672 GSS_C_QOP_DEFAULT,
673 &input_token,
674 &conf_state,
675 &output_token);
676
677 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
678 snprintf(state->emsg, sizeof(state->emsg), "%s: gss_wrap(): %s",
679 function, emsg);
680
681 CLEAN_GSS_TOKEN(output_token);
682 return NEGOTIATE_ERROR;
683 }
684 }
685 else {
686 output_token.length = 1;
687 output_token.value = &gss_server_enc;
688 }
689
690 request->auth->mdata.gssapi.state.protection = gss_server_enc;
691
692 /*
693 * RFC1961: server reply
694 *
695 * +------+------+------+.......................+
696 * + ver | mtyp | len | token |
697 * +------+------+------+.......................+
698 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
699 * +------+------+------+.......................+
700 *
701 */
702
703 SASSERTX(GSSAPI_HLEN + output_token.length <= sizeof(buf));
704
705 buflen = 0;
706 buf[buflen++] = SOCKS_GSSAPI_VERSION;
707 buf[buflen++] = SOCKS_GSSAPI_ENCRYPTION;
708
709 unsigned_short = htons(output_token.length);
710 memcpy(&buf[buflen], &unsigned_short, sizeof(unsigned_short));
711 buflen += sizeof(unsigned_short);
712
713 memcpy(&buf[buflen], output_token.value, output_token.length);
714 buflen += (output_token.length);
715
716 if ((rc = socks_sendton(s, buf, buflen, 0, 0, NULL, 0, NULL, request->auth))
717 != (ssize_t)buflen) {
718 snprintf(state->emsg, sizeof(state->emsg),
719 "socks_sendton() buf: wrote %ld out of %lu bytes: %s",
720 (long)rc,
721 (unsigned long)buflen,
722 strerror(errno));
723
724 if (do_rfc1961)
725 CLEAN_GSS_TOKEN(output_token);
726
727 return NEGOTIATE_ERROR;
728 }
729
730 if (do_rfc1961)
731 CLEAN_GSS_TOKEN(output_token);
732
733 if (gss_server_enc)
734 state->rcurrent = recv_gssapi_packet;
735 else
736 /*
737 * Continue with clear text communication.
738 * Not RFC compliant but useful if authentication only is required.
739 */
740 state->rcurrent = recv_sockspacket;
741
742 return NEGOTIATE_CONTINUE; /* presumably client is awaiting our response. */
743 }
744
745 /*
746 * RFC1961: client request
747 *
748 * +------+------+------+.......................+
749 * + ver | mtyp | len | token |
750 * +------+------+------+.......................+
751 * + 0x01 | 0x03 | 0x02 | up to 2^16 - 1 octets |
752 * +------+------+------+.......................+
753 *
754 */
755 static negotiate_result_t
recv_gssapi_packet(s,request,state)756 recv_gssapi_packet(s, request, state)
757 int s;
758 request_t *request;
759 negotiate_state_t *state;
760
761 {
762
763 state->rcurrent = recv_gssapi_packet_ver;
764 return state->rcurrent(s, request, state);
765 }
766
767 static negotiate_result_t
recv_gssapi_packet_ver(s,request,state)768 recv_gssapi_packet_ver(s, request, state)
769 int s;
770 request_t *request;
771 negotiate_state_t *state;
772 {
773 const char *function = "recv_gssapi_packet_ver()";
774 unsigned char gssapi_packet_version;
775
776 INIT(sizeof(gssapi_packet_version));
777 CHECK(&gssapi_packet_version, request->auth, NULL);
778
779 switch (gssapi_packet_version) {
780 case SOCKS_GSSAPI_VERSION:
781 break;
782
783 default:
784 snprintf(state->emsg, sizeof(state->emsg),
785 "%s: unknown version on gssapi packet from client: %d",
786 function, gssapi_packet_version);
787
788 return NEGOTIATE_ERROR;
789 }
790
791 state->rcurrent = recv_gssapi_packet_type;
792 return state->rcurrent(s, request, state);
793 }
794
795 static negotiate_result_t
recv_gssapi_packet_type(s,request,state)796 recv_gssapi_packet_type(s, request, state)
797 int s;
798 request_t *request;
799 negotiate_state_t *state;
800 {
801 const char *function = "recv_gssapi_packet_type()";
802 unsigned char gssapi_enc_type;
803
804 INIT(sizeof(gssapi_enc_type));
805 CHECK(&gssapi_enc_type, request->auth, NULL);
806
807 switch (gssapi_enc_type) {
808 case SOCKS_GSSAPI_PACKET:
809 break;
810
811 case SOCKS_GSSAPI_ENCRYPTION:
812 case SOCKS_GSSAPI_AUTHENTICATION:
813 snprintf(state->emsg, sizeof(state->emsg),
814 "%s: received out of sequence exchange from client, type: %d",
815 function, gssapi_enc_type);
816
817 return NEGOTIATE_ERROR;
818
819 default:
820 snprintf(state->emsg, sizeof(state->emsg),
821 "%s: unknown type on gssapi packet from client: %d",
822 function, gssapi_enc_type);
823
824 return NEGOTIATE_ERROR;
825 }
826
827 state->rcurrent = recv_gssapi_packet_len;
828 return state->rcurrent(s, request, state);
829 }
830
831 static negotiate_result_t
recv_gssapi_packet_len(s,request,state)832 recv_gssapi_packet_len(s, request, state)
833 int s;
834 request_t *request;
835 negotiate_state_t *state;
836 {
837
838 INIT(sizeof(state->gssapitoken_len));
839 CHECK(&state->gssapitoken_len, request->auth, NULL);
840
841 state->gssapitoken_len
842 = ntohs((short)state->gssapitoken_len);
843
844 state->rcurrent = recv_gssapi_packet_token;
845 return state->rcurrent(s, request, state);
846 }
847
848 static negotiate_result_t
recv_gssapi_packet_token(s,request,state)849 recv_gssapi_packet_token(s, request, state)
850 int s;
851 request_t *request;
852 negotiate_state_t *state;
853 {
854 const char *function = "recv_gssapi_packet_token()";
855 OM_uint32 minor_status, major_status = GSS_S_COMPLETE;
856 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
857 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
858 unsigned char *data;
859 unsigned char buf[GSSAPI_HLEN + MAXGSSAPITOKENLEN];
860 char emsg[1024];
861 int conf_state, offset;
862
863 INIT(state->gssapitoken_len);
864
865 input_token.length = state->gssapitoken_len;
866 input_token.value = buf;
867
868 CHECK(input_token.value, request->auth, NULL);
869
870 conf_state = (request->auth->mdata.gssapi.state.protection
871 == GSSAPI_CONFIDENTIALITY) ? GSS_REQ_CONF : GSS_REQ_INT;
872
873 major_status
874 = gss_unwrap(&minor_status,
875 request->auth->mdata.gssapi.state.id,
876 &input_token, &output_token, &conf_state, GSS_C_QOP_DEFAULT);
877
878 if (gss_err_isset(major_status, minor_status, emsg, sizeof(emsg))) {
879 snprintf(state->emsg, sizeof(state->emsg),
880 "%s: gss_unwrap(): %s", function, emsg);
881
882 return NEGOTIATE_ERROR;
883 }
884
885 offset = 0;
886 data = output_token.value;
887
888 if (offset + sizeof(request->version) > output_token.length) {
889 snprintf(state->emsg, sizeof(state->emsg),
890 "%s: token has short length: %lu",
891 function, (unsigned long)output_token.length);
892
893 CLEAN_GSS_TOKEN(output_token);
894 return NEGOTIATE_ERROR;
895 }
896
897 memcpy(&request->version, &data[offset], sizeof(request->version));
898 if (request->version != PROXY_SOCKS_V5) {
899 snprintf(state->emsg, sizeof(state->emsg),
900 "%s: invalid socks version %d in request", function, request->version);
901
902 CLEAN_GSS_TOKEN(output_token);
903 return NEGOTIATE_ERROR;
904 }
905 offset += sizeof(request->version);
906
907 if (offset + sizeof(request->command) > output_token.length) {
908 snprintf(state->emsg, sizeof(state->emsg),
909 "%s: token has short length: %lu",
910 function, (unsigned long)output_token.length);
911
912 CLEAN_GSS_TOKEN(output_token);
913 return NEGOTIATE_ERROR;
914 }
915 memcpy(&request->command, &data[offset], sizeof(request->command));
916 offset += sizeof(request->command);
917
918 switch (request->command) {
919 case SOCKS_BIND:
920 case SOCKS_CONNECT:
921 request->protocol = SOCKS_TCP;
922 break;
923
924 case SOCKS_UDPASSOCIATE:
925 request->protocol = SOCKS_UDP;
926 break;
927
928 default:
929 snprintf(state->emsg, sizeof(state->emsg),
930 "%s: unknown command received from client: %d",
931 function, request->command);
932
933 CLEAN_GSS_TOKEN(output_token);
934 return NEGOTIATE_ERROR;
935 }
936
937 if (offset + sizeof(request->flag) > output_token.length) {
938 CLEAN_GSS_TOKEN(output_token);
939 return NEGOTIATE_ERROR;
940 }
941 memcpy(&request->flag, &data[offset], sizeof(request->flag));
942 offset += sizeof(request->flag);
943
944 if (mem2sockshost(&request->host, &data[offset],
945 output_token.length - offset, request->version) == NULL) {
946 CLEAN_GSS_TOKEN(output_token);
947 return NEGOTIATE_ERROR;
948 }
949
950 CLEAN_GSS_TOKEN(output_token);
951
952 /* Negotiation finished => set connection state as protected */
953 if (request->auth->mdata.gssapi.state.protection)
954 request->auth->mdata.gssapi.state.wrap = 1;
955
956 return NEGOTIATE_FINISHED;
957 }
958
959 #endif /* HAVE_GSSAPI */
960