/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "k5-int.h"
#include "int-proto.h"
/* Solaris Kerberos */
extern krb5_error_code krb5_libdefault_boolean();
static krb5_error_code
krb5_cc_copy_creds_except(krb5_context context, krb5_ccache incc, krb5_ccache outcc, krb5_principal princ)
{
krb5_error_code code;
krb5_flags flags;
krb5_cc_cursor cur;
krb5_creds creds;
flags = 0; /* turns off OPENCLOSE mode */
/* Solaris Kerberos */
if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL)
return(code);
/* Solaris Kerberos */
if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL)
return(code);
/* Solaris Kerberos */
if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL)
goto cleanup;
/* Solaris Kerberos */
while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) {
if (krb5_principal_compare(context, princ, creds.server))
continue;
code = krb5_cc_store_cred(context, outcc, &creds);
krb5_free_cred_contents(context, &creds);
if (code)
goto cleanup;
}
if (code != KRB5_CC_END)
goto cleanup;
code = 0;
cleanup:
flags = KRB5_TC_OPENCLOSE;
/* Solaris Kerberos */
if (code)
(void) krb5_cc_set_flags(context, incc, flags);
else
code = krb5_cc_set_flags(context, incc, flags);
/* Solaris Kerberos */
if (code)
(void) krb5_cc_set_flags(context, outcc, flags);
else
code = krb5_cc_set_flags(context, outcc, flags);
return(code);
}
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;
/* Solaris Kerberos */
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);
}
/*
* Solaris Kerberos:
* 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 */
/* Solaris Kerberos */
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 {
/*
* Solaris Kerberos:
* 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;
/* Solaris Kerberos */
if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL)
goto cleanup;
/* Solaris Kerberos */
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;
/* Solaris Kerberos */
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)) {
/* Solaris Kerberos */
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);
/* Solaris Kerberos */
if (!keytab_arg && keytab)
(void) krb5_kt_close(context, keytab);
/* Solaris Kerberos */
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);
}