int main(int argc, char * const argv[]) { char buf[MAX_AUTHTOKEN_LEN]; char *c; char *user=NULL; int length=0; static int err=0; int opt, rc, debug=0, loging=0; OM_uint32 ret_flags=0, spnego_flag=0; char *service_name=(char *)"HTTP",*host_name=NULL; char *token = NULL; char *service_principal = NULL; OM_uint32 major_status, minor_status; gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; gss_name_t client_name = GSS_C_NO_NAME; gss_name_t server_name = GSS_C_NO_NAME; gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; const unsigned char *kerberosToken = NULL; size_t kerberosTokenLength = 0; const unsigned char *spnegoToken = NULL ; size_t spnegoTokenLength = 0; setbuf(stdout,NULL); setbuf(stdin,NULL); while (-1 != (opt = getopt(argc, argv, "dis:h"))) { switch (opt) { case 'd': debug = 1; break; case 'i': loging = 1; break; case 's': service_principal = strdup(optarg); break; case 'h': fprintf(stdout, "Usage: \n"); fprintf(stdout, "squid_kerb_auth -d [-s SPN]\n"); fprintf(stdout, "SPN = service principal name\n"); fprintf(stdout, "Can be set to GSS_C_NO_NAME to allow any entry from keytab\n"); fprintf(stdout, "default SPN is HTTP/fqdn@DEFAULT_REALM\n"); break; default: fprintf(stderr, "%s| %s: unknown option: -%c.\n", LogTime(), PROGRAM, opt); } } if (service_principal && strcasecmp(service_principal,"GSS_C_NO_NAME") ) { service.value = service_principal; service.length = strlen((char *)service.value); } else { host_name=gethost_name(); if ( !host_name ) { fprintf(stderr, "%s| %s: Local hostname could not be determined. Please specify the service principal\n", LogTime(), PROGRAM); exit(-1); } service.value = malloc(strlen(service_name)+strlen(host_name)+2); snprintf(service.value,strlen(service_name)+strlen(host_name)+2,"%s@%s",service_name,host_name); service.length = strlen((char *)service.value); } while (1) { if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { if (ferror(stdin)) { if (debug) fprintf(stderr, "%s| %s: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM, ferror(stdin), strerror(ferror(stdin))); exit(1); /* BIIG buffer */ } exit(0); } c=memchr(buf,'\n',sizeof(buf)-1); if (c) { *c = '\0'; length = c-buf; } else { err = 1; } if (err) { if (debug) fprintf(stderr, "%s| %s: Oversized message\n", LogTime(), PROGRAM); fprintf(stdout, "NA Oversized message\n"); err = 0; continue; } if (debug) fprintf(stderr, "%s| %s: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf ,length); if (buf[0] == '\0') { if (debug) fprintf(stderr, "%s| %s: Invalid request\n", LogTime(), PROGRAM); fprintf(stdout, "NA Invalid request\n"); continue; } if (strlen(buf) < 2) { if (debug) fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid request\n"); continue; } if ( !strncmp(buf, "QQ", 2) ) { gss_release_buffer(&minor_status, &input_token); gss_release_buffer(&minor_status, &output_token); gss_release_buffer(&minor_status, &service); gss_release_cred(&minor_status, &server_creds); gss_release_cred(&minor_status, &delegated_cred); gss_release_name(&minor_status, &server_name); gss_release_name(&minor_status, &client_name); gss_delete_sec_context(&minor_status, &gss_context, NULL); if (kerberosToken) { /* Allocated by parseNegTokenInit, but no matching free function exists.. */ if (!spnego_flag) free((char *)kerberosToken); kerberosToken=NULL; } if (spnego_flag) { /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ if (spnegoToken) free((char *)spnegoToken); spnegoToken=NULL; } if (token) { free(token); token=NULL; } if (host_name) { free(host_name); host_name=NULL; } exit(0); } if ( !strncmp(buf, "YR", 2) && !strncmp(buf, "KK", 2) ) { if (debug) fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid request\n"); continue; } if ( !strncmp(buf, "YR", 2) ){ if (gss_context != GSS_C_NO_CONTEXT ) gss_delete_sec_context(&minor_status, &gss_context, NULL); gss_context = GSS_C_NO_CONTEXT; } if (strlen(buf) <= 3) { if (debug) fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid negotiate request\n"); continue; } input_token.length = base64_decode_len(buf+3); input_token.value = malloc(input_token.length); base64_decode(input_token.value,buf+3,input_token.length); #ifndef HAVE_SPNEGO if (( rc=parseNegTokenInit (input_token.value, input_token.length, &kerberosToken, &kerberosTokenLength))!=0 ){ if (debug) fprintf(stderr, "%s| %s: parseNegTokenInit failed with rc=%d\n", LogTime(), PROGRAM, rc); /* if between 100 and 200 it might be a GSSAPI token and not a SPNEGO token */ if ( rc < 100 || rc > 199 ) { if (debug) fprintf(stderr, "%s| %s: Invalid GSS-SPNEGO query [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid GSS-SPNEGO query\n"); goto cleanup; } if ((input_token.length >= sizeof ntlmProtocol + 1) && (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { if (debug) fprintf(stderr, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM, (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); fprintf(stdout, "NA received type %d NTLM token\n",(int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); goto cleanup; } spnego_flag=0; } else { gss_release_buffer(&minor_status, &input_token); input_token.length=kerberosTokenLength; input_token.value=(void *)kerberosToken; spnego_flag=1; } #else if ((input_token.length >= sizeof ntlmProtocol + 1) && (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { if (debug) fprintf(stderr, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM, (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); fprintf(stdout, "NA received type %d NTLM token\n",(int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); goto cleanup; } #endif if ( service_principal ) { if ( strcasecmp(service_principal,"GSS_C_NO_NAME") ){ major_status = gss_import_name(&minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server_name); } else { server_name = GSS_C_NO_NAME; major_status = GSS_S_COMPLETE; } } else { major_status = gss_import_name(&minor_status, &service, gss_nt_service_name, &server_name); } if ( check_gss_err(major_status,minor_status,"gss_import_name()",debug,loging) ) goto cleanup; major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); if (check_gss_err(major_status,minor_status,"gss_acquire_cred()",debug,loging) ) goto cleanup; major_status = gss_accept_sec_context(&minor_status, &gss_context, server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &client_name, NULL, &output_token, &ret_flags, NULL, &delegated_cred); if (output_token.length) { #ifndef HAVE_SPNEGO if (spnego_flag) { if ((rc=makeNegTokenTarg (output_token.value, output_token.length, &spnegoToken, &spnegoTokenLength))!=0 ) { if (debug) fprintf(stderr, "%s| %s: makeNegTokenTarg failed with rc=%d\n", LogTime(), PROGRAM, rc); fprintf(stdout, "NA makeNegTokenTarg failed with rc=%d\n",rc); goto cleanup; } } else { spnegoToken = output_token.value; spnegoTokenLength = output_token.length; } #else spnegoToken = output_token.value; spnegoTokenLength = output_token.length; #endif token = malloc(base64_encode_len(spnegoTokenLength)); if (token == NULL) { if (debug) fprintf(stderr, "%s| %s: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "NA Not enough memory\n"); goto cleanup; } base64_encode(token,(const char *)spnegoToken,base64_encode_len(spnegoTokenLength),spnegoTokenLength); if (check_gss_err(major_status,minor_status,"gss_accept_sec_context()",debug,loging) ) goto cleanup; if (major_status & GSS_S_CONTINUE_NEEDED) { if (debug) fprintf(stderr, "%s| %s: continuation needed\n", LogTime(), PROGRAM); fprintf(stdout, "TT %s\n",token); goto cleanup; } gss_release_buffer(&minor_status, &output_token); major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); if (check_gss_err(major_status,minor_status,"gss_display_name()",debug,loging) ) goto cleanup; user=malloc(output_token.length+1); if (user == NULL) { if (debug) fprintf(stderr, "%s| %s: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } memcpy(user,output_token.value,output_token.length); user[output_token.length]='\0'; fprintf(stdout, "AF %s %s\n",token,user); if (debug) fprintf(stderr, "%s| %s: AF %s %s\n", LogTime(), PROGRAM, token,user); if (loging) fprintf(stderr, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM, user); goto cleanup; } else { if (check_gss_err(major_status,minor_status,"gss_accept_sec_context()",debug,loging) ) goto cleanup; if (major_status & GSS_S_CONTINUE_NEEDED) { if (debug) fprintf(stderr, "%s| %s: continuation needed\n", LogTime(), PROGRAM); fprintf(stdout, "NA No token to return to continue\n"); goto cleanup; } gss_release_buffer(&minor_status, &output_token); major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); if (check_gss_err(major_status,minor_status,"gss_display_name()",debug,loging) ) goto cleanup; /* * Return dummy token AA. May need an extra return tag then AF */ user=malloc(output_token.length+1); if (user == NULL) { if (debug) fprintf(stderr, "%s| %s: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } memcpy(user,output_token.value,output_token.length); user[output_token.length]='\0'; fprintf(stdout, "AF %s %s\n","AA==",user); if (debug) fprintf(stderr, "%s| %s: AF %s %s\n", LogTime(), PROGRAM, "AA==", user); if (loging) fprintf(stderr, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM, user); cleanup: gss_release_buffer(&minor_status, &input_token); gss_release_buffer(&minor_status, &output_token); gss_release_cred(&minor_status, &server_creds); gss_release_cred(&minor_status, &delegated_cred); gss_release_name(&minor_status, &server_name); gss_release_name(&minor_status, &client_name); if (kerberosToken) { /* Allocated by parseNegTokenInit, but no matching free function exists.. */ if (!spnego_flag) free((char *)kerberosToken); kerberosToken=NULL; } if (spnego_flag) { /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ if (spnegoToken) free((char *)spnegoToken); spnegoToken=NULL; } if (token) { free(token); token=NULL; } if( user) { free(user); user=NULL; } continue; } } }
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, struct connectdata *conn) { struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[sockindex]; CURLcode code; ssize_t actualread; ssize_t written; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc* gss_token = GSS_C_NO_BUFFER; gss_name_t server = GSS_C_NO_NAME; gss_name_t gss_client_name = GSS_C_NO_NAME; unsigned short us_length; char *user=NULL; unsigned char socksreq[4]; /* room for gssapi exchange header only */ char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE]; /* GSSAPI request looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ /* prepare service name */ if(strchr(serviceptr,'/')) { service.value = malloc(strlen(serviceptr)); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr); memcpy(service.value, serviceptr, service.length); gss_major_status = gss_import_name(&gss_minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server); } else { service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1; snprintf(service.value, service.length+1, "%s@%s", serviceptr, conn->proxy.name); gss_major_status = gss_import_name(&gss_minor_status, &service, gss_nt_service_name, &server); } gss_release_buffer(&gss_status, &service); /* clear allocated memory */ if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_import_name()")) { failf(data, "Failed to create service name."); gss_release_name(&gss_status, &server); return CURLE_COULDNT_CONNECT; } /* As long as we need to keep sending some context info, and there's no */ /* errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, &gss_context, server, NULL, gss_token, &gss_send_token, &gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) gss_release_buffer(&gss_status, &gss_recv_token); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_init_sec_context")) { gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to initial GSSAPI token."); return CURLE_COULDNT_CONNECT; } if(gss_send_token.length != 0) { socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 1; /* authentication message type */ us_length = htons((short)gss_send_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } code = Curl_write_plain(conn, sock, (char *)gss_send_token.value, gss_send_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) { failf(data, "Failed to send GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_recv_token); if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; /* analyse response */ /* GSSAPI response looks like * +----+------+-----+----------------+ * |VER | MTYP | LEN | TOKEN | * +----+------+----------------------+ * | 1 | 1 | 2 | up to 2^16 - 1 | * +----+------+-----+----------------+ */ result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI authentication response."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / messgae type */ failf(data, "Invalid GSSAPI authentication response type (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length=us_length; gss_recv_token.value=malloc(us_length); if(!gss_recv_token.value) { failf(data, "Could not allocate memory for GSSAPI authentication " "response token."); gss_release_name(&gss_status, &server); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } gss_release_name(&gss_status, &server); /* Everything is good so far, user was authenticated! */ gss_major_status = gss_inquire_context (&gss_minor_status, gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_inquire_context")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, &gss_send_token, NULL); if(check_gss_err(data,gss_major_status, gss_minor_status,"gss_display_name")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine user name."); return CURLE_COULDNT_CONNECT; } user=malloc(gss_send_token.length+1); if(!user) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); return CURLE_OUT_OF_MEMORY; } memcpy(user, gss_send_token.value, gss_send_token.length); user[gss_send_token.length] = '\0'; gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user); free(user); user=NULL; /* Do encryption */ socksreq[0] = 1; /* gssapi subnegotiation version */ socksreq[1] = 2; /* encryption message type */ gss_enc = 0; /* no data protection */ /* do confidentiality protection if supported */ if(gss_ret_flags & GSS_C_CONF_FLAG) gss_enc = 2; /* else do integrity protection */ else if(gss_ret_flags & GSS_C_INTEG_FLAG) gss_enc = 1; infof(data, "SOCKS5 server supports gssapi %s data protection.\n", (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality")); /* force for the moment to no data protection */ gss_enc = 0; /* * Sending the encryption type in clear seems wrong. It should be * protected with gss_seal()/gss_wrap(). See RFC1961 extract below * The NEC reference implementations on which this is based is * therefore at fault * * +------+------+------+.......................+ * + ver | mtyp | len | token | * +------+------+------+.......................+ * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | * +------+------+------+.......................+ * * Where: * * - "ver" is the protocol version number, here 1 to represent the * first version of the SOCKS/GSS-API protocol * * - "mtyp" is the message type, here 2 to represent a protection * -level negotiation message * * - "len" is the length of the "token" field in octets * * - "token" is the GSS-API encapsulated protection level * * The token is produced by encapsulating an octet containing the * required protection level using gss_seal()/gss_wrap() with conf_req * set to FALSE. The token is verified using gss_unseal()/ * gss_unwrap(). * */ if(data->set.socks5_gssapi_nec) { us_length = htons((short)1); memcpy(socksreq+2,&us_length,sizeof(short)); } else { gss_send_token.length = 1; gss_send_token.value = malloc(1); if(!gss_send_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } memcpy(gss_send_token.value, &gss_enc, 1); gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, &gss_conf_state, &gss_w_token); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) { gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_send_token); us_length = htons((short)gss_w_token.length); memcpy(socksreq+2,&us_length,sizeof(short)); } code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); if((code != CURLE_OK) || (4 != written)) { failf(data, "Failed to send GSSAPI encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); code = Curl_write_plain(conn, sock, socksreq, 1, &written); if((code != CURLE_OK) || ( 1 != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { code = Curl_write_plain(conn, sock, (char *)gss_w_token.value, gss_w_token.length, &written); if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) { failf(data, "Failed to send GSSAPI encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); } result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); if(result != CURLE_OK || actualread != 4) { failf(data, "Failed to receive GSSAPI encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } /* ignore the first (VER) byte */ if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / messgae type */ failf(data, "Invalid GSSAPI encryption response type (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq+2, sizeof(short)); us_length = ntohs(us_length); gss_recv_token.length= us_length; gss_recv_token.value=malloc(gss_recv_token.length); if(!gss_recv_token.value) { gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result != CURLE_OK || actualread != us_length) { failf(data, "Failed to receive GSSAPI encryptrion type."); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { gss_major_status = gss_unwrap(&gss_minor_status, gss_context, &gss_recv_token, &gss_w_token, 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) { gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSSAPI encryption value into token."); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_recv_token); if(gss_w_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_w_token.value,gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); } else { if(gss_recv_token.length != 1) { failf(data, "Invalid GSSAPI encryption response length (%d).", gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq,gss_recv_token.value,gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); } infof(data, "SOCKS5 access with%s protection granted.\n", (socksreq[0]==0)?"out gssapi data": ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality")); conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OK; }
int main(int argc, char * const argv[]) { char buf[MAX_AUTHTOKEN_LEN]; char *c; char *user=NULL; int length=0; static int err=0; int opt, debug=0, log=0; OM_uint32 ret_flags=0, spnego_flag=0; char *service_name=(char *)"HTTP",*host_name=NULL; char *token = NULL; char *service_principal = NULL; OM_uint32 major_status, minor_status; gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; gss_name_t client_name = GSS_C_NO_NAME; gss_name_t server_name = GSS_C_NO_NAME; gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; const unsigned char *kerberosToken = NULL; const unsigned char *spnegoToken = NULL ; size_t spnegoTokenLength = 0; setbuf(stdout,NULL); setbuf(stdin,NULL); while (-1 != (opt = getopt(argc, argv, "dis:h"))) { switch (opt) { case 'd': debug = 1; break; case 'i': log = 1; break; case 's': service_principal = xstrdup(optarg); break; case 'h': fprintf(stderr, "Usage: \n"); fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n"); fprintf(stderr, "-d full debug\n"); fprintf(stderr, "-i informational messages\n"); fprintf(stderr, "-s service principal name\n"); fprintf(stderr, "-h help\n"); fprintf(stderr, "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n"); fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n"); exit(0); default: fprintf(stderr, "%s| %s: WARNING: unknown option: -%c.\n", LogTime(), PROGRAM, opt); } } if (debug) fprintf(stderr, "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, VERSION); if (service_principal && strcasecmp(service_principal,"GSS_C_NO_NAME") ) { service.value = service_principal; service.length = strlen((char *)service.value); } else { host_name=gethost_name(); if ( !host_name ) { fprintf(stderr, "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n", LogTime(), PROGRAM); fprintf(stdout, "BH hostname error\n"); exit(-1); } service.value = xmalloc(strlen(service_name)+strlen(host_name)+2); snprintf(service.value,strlen(service_name)+strlen(host_name)+2,"%s@%s",service_name,host_name); service.length = strlen((char *)service.value); } while (1) { if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { if (ferror(stdin)) { if (debug) fprintf(stderr, "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM, ferror(stdin), strerror(ferror(stdin))); fprintf(stdout, "BH input error\n"); exit(1); /* BIIG buffer */ } fprintf(stdout, "BH input error\n"); exit(0); } c=memchr(buf,'\n',sizeof(buf)-1); if (c) { *c = '\0'; length = c-buf; } else { err = 1; } if (err) { if (debug) fprintf(stderr, "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM); fprintf(stdout, "BH Oversized message\n"); err = 0; continue; } if (debug) fprintf(stderr, "%s| %s: DEBUG: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf,length); if (buf[0] == '\0') { if (debug) fprintf(stderr, "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM); fprintf(stdout, "BH Invalid request\n"); continue; } if (strlen(buf) < 2) { if (debug) fprintf(stderr, "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "BH Invalid request\n"); continue; } if ( !strncmp(buf, "QQ", 2) ) { gss_release_buffer(&minor_status, &input_token); gss_release_buffer(&minor_status, &output_token); gss_release_buffer(&minor_status, &service); gss_release_cred(&minor_status, &server_creds); if (server_name) gss_release_name(&minor_status, &server_name); if (client_name) gss_release_name(&minor_status, &client_name); if (gss_context != GSS_C_NO_CONTEXT ) gss_delete_sec_context(&minor_status, &gss_context, NULL); if (kerberosToken) { /* Allocated by parseNegTokenInit, but no matching free function exists.. */ if (!spnego_flag) xfree((char *)kerberosToken); kerberosToken=NULL; } if (spnego_flag) { /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ if (spnegoToken) xfree((char *)spnegoToken); spnegoToken=NULL; } if (token) { xfree(token); token=NULL; } if (host_name) { xfree(host_name); host_name=NULL; } fprintf(stdout, "BH quit command\n"); exit(0); } if ( strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2) ) { if (debug) fprintf(stderr, "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "BH Invalid request\n"); continue; } if ( !strncmp(buf, "YR", 2) ) { if (gss_context != GSS_C_NO_CONTEXT ) gss_delete_sec_context(&minor_status, &gss_context, NULL); gss_context = GSS_C_NO_CONTEXT; } if (strlen(buf) <= 3) { if (debug) fprintf(stderr, "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "BH Invalid negotiate request\n"); continue; } input_token.length = ska_base64_decode_len(buf+3); if (debug) fprintf(stderr, "%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n", LogTime(), PROGRAM, buf+3,(int)input_token.length); input_token.value = xmalloc(input_token.length); ska_base64_decode(input_token.value,buf+3,input_token.length); if ((input_token.length >= sizeof ntlmProtocol + 1) && (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { if (debug) fprintf(stderr, "%s| %s: WARNING: received type %d NTLM token\n", LogTime(), PROGRAM, (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); fprintf(stdout, "BH received type %d NTLM token\n",(int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); goto cleanup; } if ( service_principal ) { if ( strcasecmp(service_principal,"GSS_C_NO_NAME") ) { major_status = gss_import_name(&minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server_name); } else { server_name = GSS_C_NO_NAME; major_status = GSS_S_COMPLETE; } } else { major_status = gss_import_name(&minor_status, &service, gss_nt_service_name, &server_name); } if ( check_gss_err(major_status,minor_status,"gss_import_name()",debug,log) ) goto cleanup; major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); if (check_gss_err(major_status,minor_status,"gss_acquire_cred()",debug,log) ) goto cleanup; major_status = gss_accept_sec_context(&minor_status, &gss_context, server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &client_name, NULL, &output_token, &ret_flags, NULL, NULL); if (output_token.length) { spnegoToken = output_token.value; spnegoTokenLength = output_token.length; token = xmalloc(ska_base64_encode_len(spnegoTokenLength)); if (token == NULL) { if (debug) fprintf(stderr, "%s| %s: ERROT: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } ska_base64_encode(token,(const char *)spnegoToken,ska_base64_encode_len(spnegoTokenLength),spnegoTokenLength); if (check_gss_err(major_status,minor_status,"gss_accept_sec_context()",debug,log) ) goto cleanup; if (major_status & GSS_S_CONTINUE_NEEDED) { if (debug) fprintf(stderr, "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM); fprintf(stdout, "TT %s\n",token); goto cleanup; } gss_release_buffer(&minor_status, &output_token); major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); if (check_gss_err(major_status,minor_status,"gss_display_name()",debug,log) ) goto cleanup; user=xmalloc(output_token.length+1); if (user == NULL) { if (debug) fprintf(stderr, "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } memcpy(user,output_token.value,output_token.length); user[output_token.length]='\0'; fprintf(stdout, "AF %s %s\n",token,user); if (debug) fprintf(stderr, "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token,user); if (log) fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(), PROGRAM, user); goto cleanup; } else { if (check_gss_err(major_status,minor_status,"gss_accept_sec_context()",debug,log) ) goto cleanup; if (major_status & GSS_S_CONTINUE_NEEDED) { if (debug) fprintf(stderr, "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM); fprintf(stdout, "NA %s\n",token); goto cleanup; } gss_release_buffer(&minor_status, &output_token); major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); if (check_gss_err(major_status,minor_status,"gss_display_name()",debug,log) ) goto cleanup; /* * Return dummy token AA. May need an extra return tag then AF */ user=xmalloc(output_token.length+1); if (user == NULL) { if (debug) fprintf(stderr, "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } memcpy(user,output_token.value,output_token.length); user[output_token.length]='\0'; fprintf(stdout, "AF %s %s\n","AA==",user); if (debug) fprintf(stderr, "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", user); if (log) fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(), PROGRAM, user); cleanup: gss_release_buffer(&minor_status, &input_token); gss_release_buffer(&minor_status, &output_token); gss_release_cred(&minor_status, &server_creds); if (server_name) gss_release_name(&minor_status, &server_name); if (client_name) gss_release_name(&minor_status, &client_name); if (kerberosToken) { /* Allocated by parseNegTokenInit, but no matching free function exists.. */ if (!spnego_flag) xfree((char *)kerberosToken); kerberosToken=NULL; } if (spnego_flag) { /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ if (spnegoToken) xfree((char *)spnegoToken); spnegoToken=NULL; } if (token) { xfree(token); token=NULL; } if (user) { xfree(user); user=NULL; } continue; } } }