summaryrefslogtreecommitdiff
path: root/cifs.upcall.c
diff options
context:
space:
mode:
authorMichael Weiser <michael.weiser@atos.net>2021-01-05 17:08:30 +0100
committerPavel Shilovsky <pshilovsky@samba.org>2022-04-29 15:07:49 -0700
commite2430c005d92e5ab19a63976c64c4294095c3ac7 (patch)
tree1ed2a5a94f3de03f3add057cf52e5f67da3b162b /cifs.upcall.c
parent58ca03f183b375cb723097a241bc2fc2254dab21 (diff)
downloadcifs-utils-e2430c005d92e5ab19a63976c64c4294095c3ac7.tar.gz
cifs-utils-e2430c005d92e5ab19a63976c64c4294095c3ac7.tar.bz2
cifs-utils-e2430c005d92e5ab19a63976c64c4294095c3ac7.zip
cifs.upcall: add gssproxy support
Add support for gssproxy usage through GSS-API. If no useable ticket cache or keytab can be found, fall on through into credential handling anyway but then divert into GSS routines. If no gssproxy is available this will still error out silently because no ticket cache is available. With gssproxy enabled, credentials can be retrieved from there and allow unattended access to shares e.g. from batch jobs. Signed-off-by: Michael Weiser <michael.weiser@atos.net>
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