Example #1
0
krb5_error_code KRB5_CALLCONV
krb5_verify_init_creds(krb5_context context,
		       krb5_creds *creds,
		       krb5_principal server_arg,
		       krb5_keytab keytab_arg,
		       krb5_ccache *ccache_arg,
		       krb5_verify_init_creds_opt *options)
{
   krb5_error_code ret;
   krb5_principal server;
   krb5_keytab keytab;
   krb5_ccache ccache;
   krb5_keytab_entry kte;
   krb5_creds in_creds, *out_creds;
   krb5_auth_context authcon;
   krb5_data ap_req;
   
   /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */

   server = NULL;
   keytab = NULL;
   ccache = NULL;
   out_creds = NULL;
   authcon = NULL;
   ap_req.data = NULL;

   if (server_arg)
      server = server_arg;
   else if (ret = krb5_sname_to_principal(context, NULL, NULL, 
					KRB5_NT_SRV_HST, &server))
      goto cleanup;
      
   /* first, check if the server is in the keytab.  If not, there's
      no reason to continue.  rd_req does all this, but there's
      no way to know that a given error is caused by a missing
      keytab or key, and not by some other problem. */

   if (keytab_arg) {
      keytab = keytab_arg;
   } else {
       /* Solaris Kerberos: ignore errors here, deal with below */
      ret = krb5_kt_default(context, &keytab);
   }

   /* Warning: be very, very careful when modifying the logic here */
   if (keytab == NULL ||
       (ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) {
       /* this means there is no keying material.  This is ok, as long as
	  it is not prohibited by the configuration */

       int nofail = 1;  /* Solaris Kerberos: default return error if keytab problems */

       if (options &&
	   (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) {
	   /* first, if options are set then use the option value to set nofail */
	    nofail = options->ap_req_nofail;
       } else {
	   /* 
	    * Check verify_ap_req_nofail if set in config file.  Note this logic
	    * assumes that krb5_libdefault_boolean will not set nofail to a
	    * default value if verify_ap_req_nofail is not explictly set in
	    * config file.  Don't care about the return code.
	    */
	   (void) krb5_libdefault_boolean(context, &creds->client->realm,
					  "verify_ap_req_nofail",
					  &nofail);
       }
       /* Solaris Kerberos: exit without an error ONLY if nofail is false */
       if (!nofail)
	   ret = 0; 

       goto cleanup;
   }

   krb5_kt_free_entry(context, &kte);

   /* If the creds are for the server principal, we're set, just do
      a mk_req.	 Otherwise, do a get_credentials first. */

   if (krb5_principal_compare(context, server, creds->server)) {
      /* make an ap_req */
      if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds,
				     &ap_req)))
	 goto cleanup;
   } else {
      /* this is unclean, but it's the easiest way without ripping the
	 library into very small pieces.  store the client's initial cred
	 in a memory ccache, then call the library.  Later, we'll copy
	 everything except the initial cred into the ccache we return to
	 the user.  A clean implementation would involve library
	 internals with a coherent idea of "in" and "out". */

      /* insert the initial cred into the ccache */

      if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache)))
	 goto cleanup;

      if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL)
	 goto cleanup;

      if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL)
	 goto cleanup;

      /* set up for get_creds */
      memset(&in_creds, 0, sizeof(in_creds));
      in_creds.client = creds->client;
      in_creds.server = server;
      if ((ret = krb5_timeofday(context, &in_creds.times.endtime)))
	 goto cleanup;
      in_creds.times.endtime += 5*60;

      if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds,
				     &out_creds)))
	 goto cleanup;

      /* make an ap_req */
      if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds,
				     &ap_req)))
	 goto cleanup;
   }

   /* wipe the auth context for mk_req */
   if (authcon) {
      krb5_auth_con_free(context, authcon);
      authcon = NULL;
   }

   /* verify the ap_req */

   if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab,
			 NULL, NULL)))
      goto cleanup;

   /* if we get this far, then the verification succeeded.  We can
      still fail if the library stuff here fails, but that's it */

   if (ccache_arg && ccache) {
       if (*ccache_arg == NULL) {
	   krb5_ccache retcc;

	   retcc = NULL;

	   if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) ||
	       ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) ||
	       ((ret = krb5_cc_copy_creds_except(context, ccache, retcc,
						creds->server)) != NULL)) {
	       if (retcc)
		   (void) krb5_cc_destroy(context, retcc);
	   } else {
	       *ccache_arg = retcc;
	   }
       } else {
	   ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg,
					   server);
       }
   }

   /* if any of the above paths returned an errors, then ret is set
      accordingly.  either that, or it's zero, which is fine, too */

cleanup:
   if (!server_arg && server)
      krb5_free_principal(context, server);
   if (!keytab_arg && keytab)
      (void) krb5_kt_close(context, keytab);
   if (ccache)
      (void) krb5_cc_destroy(context, ccache);
   if (out_creds)
      krb5_free_creds(context, out_creds);
   if (authcon)
      krb5_auth_con_free(context, authcon);
   if (ap_req.data)
      krb5_xfree(ap_req.data);

   return(ret);
}
Example #2
0
krb5_error_code KRB5_CALLCONV
krb5_verify_init_creds(krb5_context context,
		       krb5_creds *creds,
		       krb5_principal server_arg,
		       krb5_keytab keytab_arg,
		       krb5_ccache *ccache_arg,
		       krb5_verify_init_creds_opt *options)
{
   krb5_error_code ret;
   krb5_principal server;
   krb5_keytab keytab;
   krb5_ccache ccache;
   krb5_keytab_entry kte;
   krb5_creds in_creds, *out_creds;
   krb5_auth_context authcon;
   krb5_data ap_req;
   
   /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */

   server = NULL;
   keytab = NULL;
   ccache = NULL;
   out_creds = NULL;
   authcon = NULL;
   ap_req.data = NULL;

   if (server_arg) {
       ret = krb5_copy_principal(context, server_arg, &server);
       if (ret)
	   goto cleanup;
   } else {
      if ((ret = krb5_sname_to_principal(context, NULL, NULL, 
					 KRB5_NT_SRV_HST, &server)))
	 goto cleanup;
   }
      
   /* first, check if the server is in the keytab.  If not, there's
      no reason to continue.  rd_req does all this, but there's
      no way to know that a given error is caused by a missing
      keytab or key, and not by some other problem. */

   if (keytab_arg) {
      keytab = keytab_arg;
   } else {
     if ((ret = krb5_kt_default(context, &keytab)))
	 goto cleanup;
   }
   if (krb5_is_referral_realm(&server->realm)) {
       krb5_free_data_contents(context, &server->realm);
       ret = krb5_get_default_realm(context, &server->realm.data);
       if (ret) goto cleanup;
       server->realm.length = strlen(server->realm.data);
   }

   if ((ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) {
       /* this means there is no keying material.  This is ok, as long as
	  it is not prohibited by the configuration */

       int nofail;

       if (options &&
	   (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) {
	   if (options->ap_req_nofail)
	       goto cleanup;
       } else if (krb5_libdefault_boolean(context,
					  &creds->client->realm,
					  KRB5_CONF_VERIFY_AP_REQ_NOFAIL,
					  &nofail)
		  == 0) {
	   if (nofail)
	       goto cleanup;
       }

       ret = 0;
       goto cleanup;
   }

   krb5_kt_free_entry(context, &kte);

   /* If the creds are for the server principal, we're set, just do
      a mk_req.	 Otherwise, do a get_credentials first. */

   if (krb5_principal_compare(context, server, creds->server)) {
      /* make an ap_req */
      if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds,
				      &ap_req)))
	 goto cleanup;
   } else {
      /* this is unclean, but it's the easiest way without ripping the
	 library into very small pieces.  store the client's initial cred
	 in a memory ccache, then call the library.  Later, we'll copy
	 everything except the initial cred into the ccache we return to
	 the user.  A clean implementation would involve library
	 internals with a coherent idea of "in" and "out". */

      /* insert the initial cred into the ccache */

      if ((ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
	  ccache = NULL;
	  goto cleanup;
      }

      if ((ret = krb5_cc_initialize(context, ccache, creds->client)))
	 goto cleanup;

      if ((ret = krb5_cc_store_cred(context, ccache, creds)))
	 goto cleanup;

      /* set up for get_creds */
      memset(&in_creds, 0, sizeof(in_creds));
      in_creds.client = creds->client;
      in_creds.server = server;
      if ((ret = krb5_timeofday(context, &in_creds.times.endtime)))
	 goto cleanup;
      in_creds.times.endtime += 5*60;

      if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds,
				      &out_creds)))
	 goto cleanup;

      /* make an ap_req */
      if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds,
				      &ap_req)))
	 goto cleanup;
   }

   /* wipe the auth context for mk_req */
   if (authcon) {
      krb5_auth_con_free(context, authcon);
      authcon = NULL;
   }

   /* verify the ap_req */

   if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab,
			  NULL, NULL)))
      goto cleanup;

   /* if we get this far, then the verification succeeded.  We can
      still fail if the library stuff here fails, but that's it */

   if (ccache_arg && ccache) {
       if (*ccache_arg == NULL) {
	   krb5_ccache retcc;

	   retcc = NULL;

	   if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) ||
	       (ret = krb5_cc_initialize(context, retcc, creds->client)) ||
	       (ret = krb5_cc_copy_creds_except(context, ccache, retcc,
						creds->server))) {
	       if (retcc)
		   krb5_cc_destroy(context, retcc);
	   } else {
	       *ccache_arg = retcc;
	   }
       } else {
	   ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg,
					   server);
       }
   }

   /* if any of the above paths returned an errors, then ret is set
      accordingly.  either that, or it's zero, which is fine, too */

cleanup:
   if ( server)
      krb5_free_principal(context, server);
   if (!keytab_arg && keytab)
      krb5_kt_close(context, keytab);
   if (ccache)
      krb5_cc_destroy(context, ccache);
   if (out_creds)
      krb5_free_creds(context, out_creds);
   if (authcon)
      krb5_auth_con_free(context, authcon);
   if (ap_req.data)
      free(ap_req.data);

   return(ret);
}