예제 #1
0
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;            
    }
  }
}
예제 #2
0
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;
}
예제 #3
0
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;
        }
    }
}