summaryrefslogtreecommitdiff
path: root/cifs.upcall.c
diff options
context:
space:
mode:
Diffstat (limited to 'cifs.upcall.c')
-rw-r--r--cifs.upcall.c173
1 files changed, 154 insertions, 19 deletions
diff --git a/cifs.upcall.c b/cifs.upcall.c
index ad04301..e9c7f5f 100644
--- a/cifs.upcall.c
+++ b/cifs.upcall.c
@@ -36,6 +36,11 @@
#elif defined(HAVE_KRB5_H)
#include <krb5.h>
#endif
+
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+#include <sys/utsname.h>
+
#include <syslog.h>
#include <dirent.h>
#include <sys/types.h>
@@ -619,6 +624,8 @@ icfk_cleanup:
goto out;
}
+#define CIFS_SERVICE_NAME "cifs"
+
static int
cifs_krb5_get_req(const char *host, krb5_ccache ccache,
DATA_BLOB * mechtoken, DATA_BLOB * sess_key)
@@ -640,8 +647,8 @@ cifs_krb5_get_req(const char *host, krb5_ccache ccache,
return ret;
}
- ret = krb5_sname_to_principal(context, host, "cifs", KRB5_NT_UNKNOWN,
- &in_creds.server);
+ ret = krb5_sname_to_principal(context, host, CIFS_SERVICE_NAME,
+ KRB5_NT_UNKNOWN, &in_creds.server);
if (ret) {
syslog(LOG_DEBUG, "%s: unable to convert sname to princ (%s).",
__func__, host);
@@ -740,6 +747,122 @@ out_free_principal:
return ret;
}
+static void cifs_gss_display_status_1(char *m, OM_uint32 code, int type) {
+ OM_uint32 min_stat;
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx;
+
+ msg_ctx = 0;
+ while (1) {
+ (void) gss_display_status(&min_stat, code, type,
+ GSS_C_NULL_OID, &msg_ctx, &msg);
+ syslog(LOG_DEBUG, "GSS-API error %s: %s\n", m, (char *) msg.value);
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (!msg_ctx)
+ break;
+ }
+}
+
+void cifs_gss_display_status(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) {
+ cifs_gss_display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
+ cifs_gss_display_status_1(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+static int
+cifs_gss_get_req(const char *host, DATA_BLOB *mechtoken, DATA_BLOB *sess_key)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t target_name;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc output_token;
+ gss_krb5_lucid_context_v1_t *lucid_ctx = NULL;
+ gss_krb5_lucid_key_t *key = NULL;
+
+ size_t service_name_len = sizeof(CIFS_SERVICE_NAME) + 1 /* @ */ +
+ strlen(host) + 1;
+ char *service_name = malloc(service_name_len);
+ if (!service_name) {
+ syslog(LOG_DEBUG, "out of memory allocating service name");
+ goto out;
+ }
+
+ snprintf(service_name, service_name_len, "%s@%s", CIFS_SERVICE_NAME,
+ host);
+ gss_buffer_desc target_name_buf;
+ target_name_buf.value = service_name;
+ target_name_buf.length = service_name_len;
+
+ maj_stat = gss_import_name(&min_stat, &target_name_buf,
+ (gss_OID)gss_nt_service_name, &target_name);
+ free(service_name);
+ if (GSS_ERROR(maj_stat)) {
+ cifs_gss_display_status("gss_import_name", maj_stat, min_stat);
+ goto out;
+ }
+
+ maj_stat = gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL, /* claimant_cred_handle */
+ &ctx,
+ target_name,
+ gss_mech_krb5, /* force krb5 */
+ 0, /* flags */
+ 0, /* time_req */
+ GSS_C_NO_CHANNEL_BINDINGS, /* input_chan_bindings */
+ GSS_C_NO_BUFFER,
+ NULL, /* actual mech type */
+ &output_token,
+ NULL, /* ret_flags */
+ NULL); /* time_rec */
+
+ if (maj_stat != GSS_S_COMPLETE &&
+ maj_stat != GSS_S_CONTINUE_NEEDED) {
+ cifs_gss_display_status("init_sec_context", maj_stat, min_stat);
+ goto out_release_target_name;
+ }
+
+ /* as luck would have it, GSS-API hands us the finished article */
+ *mechtoken = data_blob(output_token.value, output_token.length);
+
+ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx, 1,
+ (void **)&lucid_ctx);
+
+ if (GSS_ERROR(maj_stat)) {
+ cifs_gss_display_status("gss_krb5_export_lucid_sec_context",
+ maj_stat, min_stat);
+ goto out_free_sec_ctx;
+ }
+
+ switch (lucid_ctx->protocol) {
+ case 0:
+ key = &lucid_ctx->rfc1964_kd.ctx_key;
+ break;
+ case 1:
+ if (lucid_ctx->cfx_kd.have_acceptor_subkey) {
+ key = &lucid_ctx->cfx_kd.acceptor_subkey;
+ } else {
+ key = &lucid_ctx->cfx_kd.ctx_key;
+ }
+ break;
+ default:
+ syslog(LOG_DEBUG, "wrong lucid context protocol %d",
+ lucid_ctx->protocol);
+ goto out_free_lucid_ctx;
+ }
+
+ *sess_key = data_blob(key->data, key->length);
+
+out_free_lucid_ctx:
+ (void) gss_krb5_free_lucid_sec_context(&min_stat, lucid_ctx);
+out_free_sec_ctx:
+ (void) gss_delete_sec_context(&min_stat, &ctx, GSS_C_NO_BUFFER);
+ (void) gss_release_buffer(&min_stat, &output_token);
+out_release_target_name:
+ (void) gss_release_name(&min_stat, &target_name);
+out:
+ return GSS_ERROR(maj_stat);
+}
+
/*
* Prepares AP-REQ data for mechToken and gets session key
* Uses credentials from cache. It will not ask for password
@@ -765,28 +888,45 @@ handle_krb5_mech(const char *oid, const char *host, DATA_BLOB * secblob,
DATA_BLOB * sess_key, krb5_ccache ccache)
{
int retval;
- DATA_BLOB tkt, tkt_wrapped;
+ DATA_BLOB tkt_wrapped;
syslog(LOG_DEBUG, "%s: getting service ticket for %s", __func__, host);
- /* get a kerberos ticket for the service and extract the session key */
- retval = cifs_krb5_get_req(host, ccache, &tkt, sess_key);
- if (retval) {
- syslog(LOG_DEBUG, "%s: failed to obtain service ticket (%d)",
- __func__, retval);
- return retval;
- }
+ /*
+ * Fall back to gssapi if there's no credential cache or no TGT
+ * so that gssproxy can maybe help out.
+ */
+ if (!ccache) {
+ syslog(LOG_DEBUG, "%s: using GSS-API", __func__);
+ retval = cifs_gss_get_req(host, &tkt_wrapped, sess_key);
+ if (retval) {
+ syslog(LOG_DEBUG, "%s: failed to obtain service ticket via GSS (%d)",
+ __func__, retval);
+ return retval;
+ }
+ } else {
+ DATA_BLOB tkt;
+ syslog(LOG_DEBUG, "%s: using native krb5", __func__);
+
+ /* get a kerberos ticket for the service and extract the session key */
+ retval = cifs_krb5_get_req(host, ccache, &tkt, sess_key);
+ if (retval) {
+ syslog(LOG_DEBUG, "%s: failed to obtain service ticket (%d)",
+ __func__, retval);
+ return retval;
+ }
- syslog(LOG_DEBUG, "%s: obtained service ticket", __func__);
+ syslog(LOG_DEBUG, "%s: obtained service ticket", __func__);
- /* wrap that up in a nice GSS-API wrapping */
- tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+ /* wrap that up in a nice GSS-API wrapping */
+ tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+ data_blob_free(&tkt);
+ }
/* and wrap that in a shiny SPNEGO wrapper */
*secblob = gen_negTokenInit(oid, tkt_wrapped);
data_blob_free(&tkt_wrapped);
- data_blob_free(&tkt);
return retval;
}
@@ -1369,11 +1509,6 @@ int main(const int argc, char *const argv[])
if (ccache == NULL && arg->username[0] != '\0')
ccache = init_cc_from_keytab(keytab_name, arg->username);
- if (ccache == NULL) {
- rc = 1;
- goto out;
- }
-
host = arg->hostname;
// do mech specific authorization