]> www.infradead.org Git - users/dhowells/kafs-utils.git/commitdiff
Add "kafs gssapi aklog" command to do gssapi negotiation for rxgk testing
authorDavid Howells <dhowells@redhat.com>
Tue, 1 Sep 2020 17:13:54 +0000 (18:13 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 5 May 2023 10:53:26 +0000 (11:53 +0100)
kafs/.gitignore
kafs/Makefile
kafs/gssapi_aklog.C [new file with mode: 0644]
kafs/gssapi_help.C [new file with mode: 0644]
kafs/kafs.C
rpc-api/add_key.xg [new file with mode: 0644]
rpc-api/rxgk.et [new file with mode: 0644]
rpc-api/rxgk.xg [new file with mode: 0644]
rpc-api/yfs.xg
rpc-api/yfs_common.h
rpc-api/yfs_rxgk.xg [new file with mode: 0644]

index 94f131dc7528c8fd6732e19c040ece16385ea33f..8df2ca638cdb061c911075a9ee7d347c08d0ba6b 100644 (file)
@@ -1,5 +1,6 @@
 bos.[CH]
 fs.[CH]
+gssapi.[CH]
 pts.[CH]
 vos.[CH]
 kafs
index 2c52d0f37b982d723a395662520acc4b650f55c8..13fa6077a36fb29ffd0454a04a535e6936c04419 100644 (file)
@@ -26,6 +26,11 @@ FS_SRCS := \
        fs.C \
        fs_help.C
 
+GSSAPI_SRCS := \
+       gssapi.C \
+       gssapi_aklog.C \
+       gssapi_help.C
+
 PTS_SRCS := \
        pts.C \
        pts_help.C
@@ -44,9 +49,10 @@ VOS_SRCS := \
 CORE_OBJS := $(patsubst %.C,%.o,$(CORE_SRCS))
 BOS_OBJS  :=  $(patsubst %.C,%.o,$(BOS_SRCS))
 FS_OBJS   :=  $(patsubst %.C,%.o,$(FS_SRCS))
+GSSAPI_OBJS :=  $(patsubst %.C,%.o,$(GSSAPI_SRCS))
 PTS_OBJS  :=  $(patsubst %.C,%.o,$(PTS_SRCS))
 VOS_OBJS  :=  $(patsubst %.C,%.o,$(VOS_SRCS))
-KAFS_OBJS := $(CORE_OBJS) $(BOS_OBJS) $(FS_OBJS) $(PTS_OBJS) $(VOS_OBJS)
+KAFS_OBJS := $(CORE_OBJS) $(BOS_OBJS) $(FS_OBJS) $(GSSAPI_OBJS) $(PTS_OBJS) $(VOS_OBJS)
 
 all: kafs
 
@@ -56,6 +62,9 @@ bos.C bos.H: gen_command.py $(filter-out bos.C, $(BOS_SRCS))
 fs.C fs.H: gen_command.py $(filter-out fs.C, $(FS_SRCS))
        ./gen_command.py fs $(filter-out fs.C, $(FS_SRCS))
 
+gssapi.C gssapi.H: gen_command.py $(filter-out gssapi.C, $(GSSAPI_SRCS))
+       ./gen_command.py gssapi $(filter-out gssapi.C, $(GSSAPI_SRCS))
+
 pts.C pts.H: gen_command.py $(filter-out pts.C, $(PTS_SRCS))
        ./gen_command.py pts $(filter-out pts.C, $(PTS_SRCS))
 
@@ -65,6 +74,7 @@ vos.C vos.H: gen_command.py $(filter-out vos.C, $(VOS_SRCS))
 GENFILES := \
        bos.C bos.H \
        fs.C fs.H \
+       gssapi.C gssapi.H \
        pts.C pts.H \
        vos.C vos.H
 
@@ -74,7 +84,7 @@ DEPS          := $(wildcard .*.o.d)
 ifneq ($(DEPS),)
 include $(DEPS)
 else
-$(KAFS_OBJS): bos.H fs.H pts.H vos.H
+$(KAFS_OBJS): bos.H fs.H gssapi.H pts.H vos.H
 endif
 
 %.o: %.C
@@ -83,9 +93,10 @@ endif
 LIBDIR := ../lib
 
 kafs: $(KAFS_OBJS) $(LIBDIR)/libkafs_utils.a
-       $(CXX) -o $@ $(KAFS_OBJS) -L$(LIBDIR) -lkafs_utils -lkafs_client -luuid -lfmt
+       $(CXX) -o $@ $(KAFS_OBJS) -L$(LIBDIR) -lkafs_utils -lkafs_client -luuid -lfmt \
+               -lgssapi_krb5 -lkeyutils
 
 clean:
        $(RM) *~ *.o $(DEPS)
-       $(RM) bos.[CH] fs.[CH] pts.[CH] vos.[CH]
+       $(RM) bos.[CH] fs.[CH] pts.[CH] vos.[CH] gssapi.[CH]
        $(RM) kafs
diff --git a/kafs/gssapi_aklog.C b/kafs/gssapi_aklog.C
new file mode 100644 (file)
index 0000000..5a43cdf
--- /dev/null
@@ -0,0 +1,499 @@
+/* GSSAPI-based aklog
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <iostream>
+#include <algorithm>
+#include <fmt/ostream.h>
+#include <krb5/krb5.h>
+#include <gssapi/gssapi.h>
+extern "C" {
+#define private foo
+#include <keyutils.h>
+#undef private
+}
+#include "kafs.H"
+#include "afs_xg.H"
+#include "vlservice.H"
+
+using rxrpc::ref;
+using kafs::afs::RXGK_StartParams;
+using kafs::afs::RXGK_ClientInfo;
+
+static bool debug_gss = true;
+
+#define verbose_gss(FMT, ...)                                          \
+       do { if (a_verbose) fmt::print("GSSAPI: " FMT, ## __VA_ARGS__); } while (0)
+#define debug_gss(FMT, ...)                                            \
+       do { if (debug_gss) fmt::print("GSSAPI: " FMT, ## __VA_ARGS__); } while (0)
+
+struct rxgk_nonce {
+       unsigned int nonce[kafs::afs::RXGK_NONCE_SIZE / sizeof(unsigned int)];
+
+       void set() {
+               for (size_t i = 0;
+                    i < sizeof(nonce) / sizeof(unsigned int);
+                    i += sizeof(unsigned int))
+                       nonce[i] = random();
+       }
+
+       size_t size() const { return sizeof(nonce); }
+};
+
+static size_t rxgk_get_enc_len(RXGK_ClientInfo &client_info)
+{
+       switch (client_info.enctype) {
+       case ENCTYPE_AES128_CTS_HMAC_SHA1_96:           return 128 / 8;
+       case ENCTYPE_AES256_CTS_HMAC_SHA1_96:           return 256 / 8;
+       case ENCTYPE_AES128_CTS_HMAC_SHA256_128:        return 128 / 8;
+       case ENCTYPE_AES256_CTS_HMAC_SHA384_192:        return 256 / 8;
+       case ENCTYPE_CAMELLIA128_CTS_CMAC:              return 128 / 8;
+       case ENCTYPE_CAMELLIA256_CTS_CMAC:              return 128 / 8;
+       default:
+               throw std::runtime_error(
+                       fmt::format("GSSAPI Unknown enctype {}", client_info.enctype));
+       }
+}
+
+static void throw_error(const std::string &what, OM_uint32 major, OM_uint32 minor)
+{
+       gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
+       OM_uint32 ret_minor, mc;
+
+       debug_gss("{} -> maj={:x} min={:x})\n", what, major, minor);
+
+       if (GSS_ERROR(major)) {
+               gss_display_status(&ret_minor, minor, GSS_C_MECH_CODE, GSS_C_NO_OID,
+                                  &mc, &buf);
+               std::string msg((const char *)buf.value, buf.length);
+
+               throw std::runtime_error(
+                       fmt::format("GSSAPI {}: {} [{:d},{:d}]",
+                                   what, msg, major, minor));
+       }
+}
+
+/*
+ * Invoke RxGK negotiation on the RxGK server.
+ */
+static void rxgk_negotiate(rxrpc::Call_params &gss_params,
+                          RXGK_StartParams &client_start,
+                          gss_buffer_desc &input_token,
+                          gss_buffer_desc &output_token,
+                          rxrpc::Opaque &opaque_cache,
+                          rxrpc::Opaque &_rxgk_info,
+                          bool a_verbose)
+{
+       rxrpc::Opaque input_token_buffer;
+       rxrpc::Opaque output_token_buffer;
+       rxrpc::Opaque opaque_out;
+
+       uint32_t gss_major_status;
+       uint32_t gss_minor_status;
+       rxrpc::Opaque rxgk_info;
+
+       debug_gss("-> GSSNegotiate()\n");
+
+       input_token_buffer.buffer_size = input_token.length;
+       input_token_buffer.buffer = input_token.value;
+
+       debug_gss("input token {}\n", input_token_buffer.size());
+       debug_gss("input opaque {}\n", opaque_cache.size());
+
+       verbose_gss("Invoke RXGK::GSSNegotiate()\n");
+
+       kafs::afs::YFSRXGK::GSSNegotiate(&gss_params,
+                                        client_start, input_token_buffer, opaque_cache,
+                                        output_token_buffer, opaque_out,
+                                        gss_major_status, gss_minor_status, rxgk_info);
+
+       debug_gss("GSSNegotiate() -> maj={:x} min={:x}\n", gss_major_status, gss_minor_status);
+       debug_gss("output token {}\n", output_token_buffer.size());
+       debug_gss("output opaque {}\n", opaque_out.size());
+       debug_gss("output info {}\n", rxgk_info.size());
+
+       output_token.value = output_token_buffer.buffer;
+       output_token.length = output_token_buffer.size();
+       output_token_buffer.del_buffer = false;
+
+       if (opaque_cache.del_buffer)
+               free(opaque_cache.buffer);
+       opaque_cache = opaque_cache;
+       opaque_out.del_buffer = false;
+
+       if (_rxgk_info.del_buffer)
+               free(_rxgk_info.buffer);
+       _rxgk_info = rxgk_info;
+       rxgk_info.del_buffer = false;
+}
+
+/*
+ * Unwrap the RxGK client information.
+ */
+static void rxgk_unwrap(gss_ctx_id_t &gssctx, rxrpc::Opaque &rxgk_info,
+                       RXGK_ClientInfo &client_info)
+{
+       gss_buffer_desc wrapping = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc info = GSS_C_EMPTY_BUFFER;
+       gss_qop_t qop_state;
+       OM_uint32 major, minor;
+       int conf_state;
+
+       wrapping.length = rxgk_info.size();
+       wrapping.value = rxgk_info.buffer;
+
+       debug_gss("rxgk_info: {}\n", rxgk_info.size());
+       major = gss_unwrap(&minor, gssctx, &wrapping, &info, &conf_state, &qop_state);
+       if (GSS_ERROR(major))
+               throw_error("gss_unwrap()", major, minor);
+
+       debug_gss("unwrapped: {}\n", info.length);
+
+       rxrpc::Rx_queue rxq("RXGK::GSSNegotiate");
+       rxq.client_side = true;
+       rxq.dec_amount = info.length;
+
+       rxrpc::Rx_buffer buf;
+       buf.buf = (unsigned char *)info.value;
+       buf.len = info.length;
+       buf.no_delete = true;
+       rxq.dec_buffers.push_back(buf);
+
+       kafs::afs::xdr::decode_RXGK_ClientInfo(rxq, client_info);
+
+       debug_gss("error   : {}\n", client_info.errorcode);
+       debug_gss("enctype : {}\n", client_info.enctype);
+       debug_gss("level   : {}\n", client_info.level);
+       debug_gss("lifetime: {}\n", client_info.lifetime);
+       debug_gss("bytelife: {}\n", client_info.bytelife);
+       debug_gss("expire  : {}\n", client_info.expiration);
+       debug_gss("mic     : size={}\n", client_info.mic.size());
+       debug_gss("token   : size={}\n", client_info.token.size());
+       debug_gss("s-nonce : size={}\n", client_info.server_nonce.size());
+}
+
+/*
+ * Check the MIC on the last GSSNegotiate call.
+ */
+static void rxgk_check_mic(gss_ctx_id_t &gssctx,
+                          RXGK_StartParams &rxgk_params,
+                          RXGK_ClientInfo &client_info,
+                          bool a_verbose)
+{
+       gss_qop_t qop_state;
+       OM_uint32 major, minor;
+
+       rxrpc::Resv resv = { .max_ioc = 1, .flat = true };
+       kafs::afs::xdr::reserve_RXGK_StartParams(resv, rxgk_params);
+
+       ref<rxrpc::Enc_buffer> buf = alloc_enc_buffer(resv);
+       kafs::afs::xdr::encode_RXGK_StartParams(buf, rxgk_params);
+       rxrpc::seal_buffer(buf);
+
+       gss_buffer_desc msg = {
+               .length = buf->size,
+               .value = buf->enc_buf,
+       };
+       gss_buffer_desc mic = {
+               .length = client_info.mic.size(),
+               .value = client_info.mic.buffer,
+       };
+
+       verbose_gss("Verify MIC\n");
+       major = gss_verify_mic(&minor, gssctx, &msg, &mic, &qop_state);
+       if (GSS_ERROR(major))
+               throw_error("gss_verify_mic()", major, minor);
+}
+
+/*
+ * Derive the transport key, K0.
+ */
+static void rxgk_derive_K0(gss_ctx_id_t &gssctx,
+                          RXGK_StartParams &rxgk_params,
+                          RXGK_ClientInfo &client_info,
+                          rxrpc::Opaque &K0_buffer,
+                          bool a_verbose)
+{
+       gss_buffer_desc K0 = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc prf_in;
+       OM_uint32 major, minor;
+       size_t enclen = rxgk_get_enc_len(client_info);
+       size_t csize = rxgk_params.client_nonce.size();
+       size_t ssize = client_info.server_nonce.size();
+
+       prf_in.length = csize + ssize;
+       prf_in.value = alloca(prf_in.length);
+       memcpy(prf_in.value, rxgk_params.client_nonce.buffer, csize);
+       memcpy((char *)prf_in.value + csize, client_info.server_nonce.buffer, ssize);
+
+       major = gss_pseudo_random(&minor, gssctx, GSS_C_PRF_KEY_FULL, &prf_in,
+                                 enclen, &K0);
+       if (GSS_ERROR(major))
+               throw_error("gss_pseudo_random()", major, minor);
+
+       debug_gss("K0 size {}\n", K0.length);
+
+       K0_buffer.buffer_size   = K0.length;
+       K0_buffer.buffer        = K0.value;
+       K0_buffer.del_buffer    = true;
+}
+
+static void clear_gss_buffer(gss_buffer_t buf)
+{
+       free(buf->value);
+       buf->value = NULL;
+       buf->length = 0;
+}
+
+static void rxgk_add_key(kafs::Context *ctx,
+                        RXGK_ClientInfo &client_info,
+                        rxrpc::Opaque &K0,
+                        bool a_verbose,
+                        unsigned sec_type)
+{
+       kafs::afs::ktc_tokenUnion token;
+
+       token.type = sec_type;
+       switch (sec_type) {
+       case kafs::afs::AFSTOKEN_UNION_YFSGK:
+               token.ygk.begintime     = 0;
+               token.ygk.endtime       = client_info.expiration;
+               token.ygk.level         = client_info.level;
+               token.ygk.lifetime      = client_info.lifetime;
+               token.ygk.bytelife      = client_info.bytelife;
+               token.ygk.enctype       = client_info.enctype;
+               token.ygk.key           = K0;
+               token.ygk.key.del_buffer = false;
+               token.ygk.ticket        = client_info.token;
+               token.ygk.ticket.del_buffer = false;
+               break;
+       default:
+               throw std::runtime_error("Unknown security type");
+       }
+
+       rxrpc::Resv resv = { .max_ioc = 1, .flat = true };
+       kafs::afs::xdr::reserve_ktc_tokenUnion(resv, token);
+       ref<rxrpc::Enc_buffer> buf = alloc_enc_buffer(resv);
+       kafs::afs::xdr::encode_ktc_tokenUnion(buf, token);
+       rxrpc::seal_buffer(buf);
+
+       verbose_gss("XDR encoded token {}\n", buf->size);
+
+       kafs::afs::ktc_setTokenData payload;
+       payload.flags   = 0;
+       payload.cell    = ctx->cell_name;
+       payload.tokens.resize(1);
+
+       rxrpc::Opaque &tok = payload.tokens[0];
+       tok.buffer      = buf->enc_buf;
+       tok.buffer_size = buf->size;
+       tok.del_buffer  = false;
+
+       rxrpc::Resv resv2 = { .max_ioc = 1, .flat = true };
+       kafs::afs::xdr::reserve_ktc_setTokenData(resv2, payload);
+       ref<rxrpc::Enc_buffer> buf2 = alloc_enc_buffer(resv2);
+       kafs::afs::xdr::encode_ktc_setTokenData(buf2, payload);
+       rxrpc::seal_buffer(buf2);
+
+       std::string desc = fmt::format("afs@{}", ctx->cell_name);
+
+       verbose_gss("XDR encoded payload {}\n", buf2->size);
+       verbose_gss("Key name {}\n", desc);
+
+       key_serial_t key = add_key("rxrpc", desc.c_str(), buf2->enc_buf, buf2->size,
+                                  KEY_SPEC_SESSION_KEYRING);
+       if (key == -1)
+               throw std::system_error(
+                       std::error_code(errno, std::generic_category()),
+                       fmt::format("GSSAPI: Failed to add key '{}'", desc));
+}
+
+/***
+ * COMMAND: gssapi aklog - Authenticate via GSSAPI for RxGK
+ * ARG: "[-cell <cell name>]"
+ * ARG: "[-principal <principal name>]"
+ * ARG: "[-enctypes <enc>+]"
+ * ARG: "[-levels <levels>+]"
+ * ARG: "[-noauth]"                            - Auth
+ * ARG: "[-localauth]"                         - Auth
+ * ARG: "[-verbose]"
+ * ARG: "[-encrypt]"                           - Auth
+ *
+ * Authenticate via GSSAPI to get tokens for the RxGK security class.
+ */
+void COMMAND_gssapi_aklog(
+       kafs::Context                   *ctx,
+       std::string                     &a_principal,
+       std::vector<std::string>        &a_enctypes,
+       std::vector<std::string>        &a_levels,
+       bool                            a_verbose)
+{
+       gss_buffer_desc token_for_gssapi = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc token_for_rxgk = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
+       gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
+       gss_name_t target_name = GSS_C_NO_NAME;
+       gss_OID actual_mech = GSS_C_NO_OID;
+       OM_uint32 major, minor, req_flags, ret_flags;
+       bool anon = false;
+
+       ref<kafs::VL_service> vlservice = new kafs::VL_service(ctx);
+       rxrpc::Opaque opaque_cache, rxgk_info, K0;
+       RXGK_ClientInfo client_info;
+
+       rxrpc::Call_params gss_params = vlservice->vl_params;
+       gss_params.peer.srx_service = kafs::afs::YFSRXGK_SERVICE_ID;
+       gss_params.security = NULL;
+
+       rxgk_nonce nonce;
+       nonce.set();
+
+       RXGK_StartParams rxgk_params;
+       if (a_enctypes.size() == 0) {
+               rxgk_params.enctypes.push_back(ENCTYPE_AES128_CTS_HMAC_SHA1_96);
+               rxgk_params.enctypes.push_back(ENCTYPE_AES256_CTS_HMAC_SHA1_96);
+               rxgk_params.enctypes.push_back(ENCTYPE_AES128_CTS_HMAC_SHA256_128);
+               rxgk_params.enctypes.push_back(ENCTYPE_AES256_CTS_HMAC_SHA384_192);
+               rxgk_params.enctypes.push_back(ENCTYPE_CAMELLIA128_CTS_CMAC);
+               rxgk_params.enctypes.push_back(ENCTYPE_CAMELLIA256_CTS_CMAC);
+       } else {
+               for (size_t i = 0; i < a_enctypes.size(); i++) {
+                       std::string &enc = a_enctypes[i];
+                       rxgk_params.enctypes.push_back(stoi(enc));
+               }
+       }
+
+       if (a_levels.size() == 0) {
+               rxgk_params.levels.push_back(kafs::afs::RXGK_LEVEL_CLEAR);
+               rxgk_params.levels.push_back(kafs::afs::RXGK_LEVEL_AUTH);
+               rxgk_params.levels.push_back(kafs::afs::RXGK_LEVEL_CRYPT);
+       } else {
+               for (size_t i = 0; i < a_levels.size(); i++) {
+                       std::string &level = a_levels[i];
+                       if (level == "a")
+                               rxgk_params.levels.push_back(kafs::afs::RXGK_LEVEL_CLEAR);
+                       else if (level == "i")
+                               rxgk_params.levels.push_back(kafs::afs::RXGK_LEVEL_AUTH);
+                       else if (level == "c")
+                               rxgk_params.levels.push_back(kafs::afs::RXGK_LEVEL_CRYPT);
+                       else
+                               throw std::invalid_argument("Unknown level");
+               }
+       }
+
+       rxgk_params.lifetime = 0;
+       rxgk_params.bytelife = 0;
+       rxgk_params.client_nonce.buffer_size = nonce.size();
+       rxgk_params.client_nonce.buffer = nonce.nonce;
+
+       try {
+               std::string name = fmt::format("yfs-rxgk@_afs.{}", ctx->cell_name);
+
+               verbose_gss("Name: {}\n", name);
+
+               name_buf.value = (void *)name.c_str();
+               name_buf.length = name.size();
+               major = gss_import_name(&minor, &name_buf,
+                                       GSS_C_NT_HOSTBASED_SERVICE, &target_name);
+               if (GSS_ERROR(major))
+                       throw std::runtime_error("GSSAPI: Could not import name");
+
+               /* Mutual authentication will require a token from acceptor to
+                * initiator and thus a second call to gss_init_sec_context().
+                */
+               req_flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; // | GSS_C_CONF_FLAG
+               if (anon)
+                       req_flags |= GSS_C_ANON_FLAG;
+
+               for (;;) {
+                       verbose_gss("Invoke gss_init_sec_context()\n");
+
+                       /* The initiator_cred_handle, mech_type, time_req,
+                        * input_chan_bindings, actual_mech_type, and time_rec
+                        * parameters are not needed in many cases.  We pass
+                        * GSS_C_NO_CREDENTIAL, GSS_C_NO_OID, 0, NULL, NULL,
+                        * and NULL for them, respectively.
+                        */
+                       major = gss_init_sec_context(
+                               &minor,
+                               GSS_C_NO_CREDENTIAL,    /* Claimant cred */
+                               &gssctx,
+                               target_name,            /* Target name */
+                               GSS_C_NO_OID,           /* Mech type */
+                               req_flags,              /* Request flags */
+                               GSS_C_INDEFINITE,       /* Lifetime req */
+                               GSS_C_NO_CHANNEL_BINDINGS, /* Input chan bindings */
+                               &token_for_gssapi,      /* Input token */
+                               &actual_mech,           /* Actual mech type */
+                               &token_for_rxgk,        /* Output token */
+                               &ret_flags,             /* Return flags */
+                               NULL);                  /* Lifetime_rec */
+
+                       if (GSS_ERROR(major)) {
+                               /* Should really call rxgk_negotiate() again
+                                * here to transfer the error to the server.
+                                */
+                               throw_error("gss_init_sec_context()", major, minor);
+                       }
+
+                       clear_gss_buffer(&token_for_gssapi);
+
+                       /* Check that an anonymous initiator retained their
+                        * anonymity.
+                        */
+                       if (anon && !(ret_flags & GSS_C_ANON_FLAG))
+                               throw std::runtime_error(
+                                       "GSSAPI: Anonymous requested but not available");
+
+                       /* Always send a token if we are expecting another input token
+                        * (GSS_S_CONTINUE_NEEDED is set) or if it is nonempty.
+                        */
+                       if ((major & GSS_S_CONTINUE_NEEDED) ||
+                           token_for_rxgk.length > 0)
+                               rxgk_negotiate(gss_params, rxgk_params,
+                                              token_for_rxgk, token_for_gssapi,
+                                              opaque_cache, rxgk_info, a_verbose);
+
+                       gss_release_buffer(&minor, &token_for_rxgk);
+
+                       if (major & GSS_S_CONTINUE_NEEDED)
+                               continue;
+
+                       if (major == GSS_S_COMPLETE)
+                               break;
+
+                       /* This situation is forbidden by RFC 2743.  Bail out. */
+                       throw std::runtime_error(
+                               "GSSAPI: major not complete or continue but not error");
+               }
+
+               if ((ret_flags & req_flags) != req_flags)
+                       throw std::runtime_error(
+                               "GSSAPI: Negotiated context does not support requested flags");
+
+               rxgk_unwrap(gssctx, rxgk_info, client_info);
+               rxgk_check_mic(gssctx, rxgk_params, client_info, a_verbose);
+               rxgk_derive_K0(gssctx, rxgk_params, client_info, K0, a_verbose);
+
+               rxgk_add_key(ctx, client_info, K0, a_verbose,
+                            kafs::afs::AFSTOKEN_UNION_YFSGK);
+
+               verbose_gss("Negotiation successful\n");
+       } catch (...) {
+               gss_release_buffer(&minor, &token_for_rxgk);
+               gss_delete_sec_context(&minor, &gssctx, NULL);
+               gss_release_name(&minor, &target_name);
+               throw;
+       }
+
+       gss_release_buffer(&minor, &token_for_rxgk);
+       gss_delete_sec_context(&minor, &gssctx, NULL);
+       gss_release_name(&minor, &target_name);
+}
diff --git a/kafs/gssapi_help.C b/kafs/gssapi_help.C
new file mode 100644 (file)
index 0000000..dfdad8d
--- /dev/null
@@ -0,0 +1,48 @@
+/* The gssapi help command
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include "gssapi.H"
+
+/***
+ * COMMAND: gssapi help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+void COMMAND_gssapi_help(
+       kafs::Context                   *ctx,
+       std::vector<std::string>        &a_topic,
+       bool                            a_admin)
+{
+       if (a_topic.empty())
+               return kafs::help(&gen_gssapi_suite);
+       for (std::vector<std::string>::iterator i = a_topic.begin(); i != a_topic.end(); i++)
+               kafs::help(&gen_gssapi_suite, *i);
+}
+
+/***
+ * COMMAND: gssapi apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+void COMMAND_gssapi_apropos(
+       kafs::Context           *ctx,
+       std::string             &a_topic)
+{
+       const kafs::Command *cmd;
+       int i;
+
+       for (i = 0; gen_gssapi_suite.commands[i]; i++) {
+               cmd = gen_gssapi_suite.commands[i];
+
+               if (strstr(cmd->name, a_topic.c_str()) ||
+                   strstr(cmd->help, a_topic.c_str()))
+                       printf("%s: %s\n", cmd->name, cmd->help);
+       }
+}
index c028b55b68d74c096b2d028eaf8aedefc717fbf6..f3a5396a04d44650bf95e2e0d88093d854c8e0e0 100644 (file)
@@ -24,6 +24,7 @@ extern "C" {
 #include "arg_parse.H"
 #include "bos.H"
 #include "fs.H"
+#include "gssapi.H"
 #include "pts.H"
 #include "vos.H"
 #include "display.H"
@@ -43,6 +44,7 @@ static ref<kafs::Context> context;
 const kafs::Suite *kafs::command_suites[] = {
        &gen_bos_suite,
        &gen_fs_suite,
+       &gen_gssapi_suite,
        &gen_pts_suite,
        &gen_vos_suite,
        NULL
diff --git a/rpc-api/add_key.xg b/rpc-api/add_key.xg
new file mode 100644 (file)
index 0000000..5290839
--- /dev/null
@@ -0,0 +1,56 @@
+/* afstoken - pioctl interface for new common token data structures. -*- c -*-
+ *
+ * revised following suggestions from lha@kth.se 20070511, 20070513
+ */
+
+/* this file is in the public domain.  Do what thou wilt. */
+
+typedef int64_t opr_time;
+
+const AFSTOKEN_RK_TIX_MAX = 12000;     /* Matches entry in rxkad.h */
+
+struct token_rxkad {
+    afs_int32 viceid;
+    afs_int32 kvno;
+    afs_int64 key;
+    afs_int32 begintime;
+    afs_int32 endtime;
+    afs_int32 primary_flag;
+    opaque ticket<AFSTOKEN_RK_TIX_MAX>;
+};
+
+struct token_yfs_rxgk {
+    opr_time begintime;
+    opr_time endtime;
+    afs_int64 level;
+    afs_int64 lifetime;
+    afs_int64 bytelife;
+    afs_int64 enctype;
+    opaque key<>;
+    opaque ticket<>;
+};
+
+const AFSTOKEN_UNION_NOAUTH = 0;
+const AFSTOKEN_UNION_KAD = 2;
+const AFSTOKEN_UNION_YFSGK = 6;
+
+union ktc_tokenUnion switch (afs_int32 type) {
+    case AFSTOKEN_UNION_KAD:
+       token_rxkad     kad;
+    case AFSTOKEN_UNION_YFSGK:
+       token_yfs_rxgk  ygk;
+};
+
+const AFSTOKEN_LENGTH_MAX = 16384;
+typedef opaque token_opaque<AFSTOKEN_LENGTH_MAX>;
+
+const AFSTOKEN_EX_SETPAG = 0x00000001; /* set tokens in new pag */
+const AFSTOKEN_EX_SYSTEM = 0x00000002; /* system default token */
+const AFSTOKEN_MAX = 8;
+const AFSTOKEN_CELL_MAX = 64;
+
+struct ktc_setTokenData {
+    afs_int32 flags;
+    string cell<AFSTOKEN_CELL_MAX>;
+    token_opaque tokens<AFSTOKEN_MAX>;
+};
diff --git a/rpc-api/rxgk.et b/rpc-api/rxgk.et
new file mode 100644 (file)
index 0000000..d4a47db
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Auristor/YFS fileserver and cache manager opcodes.  Please do not add or
+ * modify opcodes here without consulting Auristor, Inc..
+ *
+ * Copyright (c) 2015-2020 AuriStor, Inc. <https://www.auristor.com/>
+ * 255 W 94th St, New York NY 10025-6985 United States, Earth, Sol, Milky Way
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+error_table RXGK
+  ec RXGKINCONSISTENCY, "Security module structure inconsistent"
+  ec RXGKPACKETSHORT, "Packet too short for security challenge"
+  ec RXGKBADCHALLENGE, "Security challenge/response failed"
+  ec RXGKSEALEDINCON, "Sealed data is inconsistent"
+  ec RXGKNOTAUTH, "Caller not authorised"
+  ec RXGKEXPIRED, "Authentication expired"
+  ec RXGKBADLEVEL, "Unsupported or not permitted security level"
+  ec RXGKBADKEYNO, "Bad transport key number"
+  ec RXGKNOTRXGK, "Security layer is not rxgk"
+  ec RXGKUNSUPPORTED, "Endpoint does not support rxgk"
+  ec RXGKGSSERROR, "GSSAPI mechanism error"
+end
diff --git a/rpc-api/rxgk.xg b/rpc-api/rxgk.xg
new file mode 100644 (file)
index 0000000..a1dab01
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- c -*-
+ * RxGK support pieces.
+ *
+ * From: https://tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11
+ */
+
+package RXGK_
+
+const RXGK_SERVICE             = 34567;
+
+const RXGK_CLIENT_ENC_PACKET   = 1026;
+const RXGK_CLIENT_MIC_PACKET   = 1027;
+const RXGK_SERVER_ENC_PACKET   = 1028;
+const RXGK_SERVER_MIC_PACKET   = 1029;
+const RXGK_CLIENT_ENC_RESPONSE = 1030;
+const RXGK_SERVER_ENC_TOKEN    = 1036;
+
+enum RXGK_Level {
+       RXGK_LEVEL_CLEAR        = 0,
+       RXGK_LEVEL_AUTH         = 1,
+       RXGK_LEVEL_CRYPT        = 2
+};
+
+/* Error codes */
+const RXGK_INCONSISTENCY       = 1233242880; /* Security module structure inconsistent */
+const RXGK_PACKETSHORT         = 1233242881; /* Packet too short for security challenge */
+const RXGK_BADCHALLENGE                = 1233242882; /* Invalid security challenge */
+const RXGK_BADETYPE            = 1233242883; /* Invalid or impermissible encryption type */
+const RXGK_BADLEVEL            = 1233242884; /* Invalid or impermissible security level */
+const RXGK_BADKEYNO            = 1233242885; /* Key version number not found */
+const RXGK_EXPIRED             = 1233242886; /* Token has expired */
+const RXGK_NOTAUTH             = 1233242887; /* Caller not authorized */
+const RXGK_BAD_TOKEN           = 1233242888; /* Security object was passed a bad token */
+const RXGK_SEALED_INCON                = 1233242889; /* Sealed data inconsistent */
+const RXGK_DATA_LEN            = 1233242890; /* User data too long */
+const RXGK_BAD_QOP             = 1233242891; /* Inadequate quality of protection available */
+
+/* limits for variable-length arrays */
+const RXGK_MAXENCTYPES         = 255;
+const RXGK_MAXLEVELS           = 255;
+const RXGK_MAXMIC              = 1024;
+const RXGK_MAXNONCE            = 1024;
+const RXGK_MAXDATA             = 1048576;
+
+typedef int32_t RXGK_Enctypes<RXGK_MAXENCTYPES>;
+typedef opaque RXGK_Data<RXGK_MAXDATA>;
+typedef int64_t rxgkTime;
+
+struct RXGK_StartParams {
+       RXGK_Enctypes   enctypes;
+       RXGK_Level      levels<RXGK_MAXLEVELS>;
+       uint32_t        lifetime;
+       uint32_t        bytelife;
+       opaque          client_nonce<RXGK_MAXNONCE>;
+};
+
+struct RXGK_ClientInfo {
+       int32_t         errorcode;
+       int32_t         enctype;
+       RXGK_Level      level;
+       uint32_t        lifetime;
+       uint32_t        bytelife;
+       rxgkTime        expiration;
+       opaque          mic<RXGK_MAXMIC>;
+       opaque          token<RXGK_MAXDATA>;
+       opaque          server_nonce<RXGK_MAXNONCE>;
+};
+
+GSSNegotiate(IN RXGK_StartParams       *client_start,
+            IN RXGK_Data               *input_token_buffer,
+            IN RXGK_Data               *opaque_in,
+            OUT RXGK_Data              *output_token_buffer,
+            OUT RXGK_Data              *opaque_out,
+            OUT uint32_t               *gss_major_status,
+            OUT uint32_t               *gss_minor_status,
+            OUT RXGK_Data              *rxgk_info
+            ) = 1;
+
+struct RXGK_CombineOptions {
+       RXGK_Enctypes   enctypes;
+       RXGK_Level      levels<RXGK_MAXLEVELS>;
+};
+
+struct RXGK_TokenInfo {
+       int32_t         enctype;
+       RXGK_Level      level;
+       uint32_t        lifetime;
+       uint32_t        bytelife;
+       rxgkTime        expiration;
+};
+
+CombineTokens(IN RXGK_Data             *token0,
+             IN RXGK_Data              *token1,
+              IN RXGK_CombineOptions   *options,
+              OUT RXGK_Data            *new_token,
+              OUT RXGK_TokenInfo       *info) = 2;
index 468808f574d42f61684fc94d7f8183f5fbe46812..87f16eb1449bfb8d4e504fbf6e2772ee74a3ce11 100644 (file)
@@ -122,8 +122,6 @@ typedef YFSFetchStatus YFSBulkStats<YFSCBMAX>;
 
 typedef int64_t YFSViceIds<FLUSHMAX>;
 
-const AUTHDATAMAX = 1024;
-const AUTHPRINTABLEMAX = 1024;
 struct FS_AuthName {
        int32_t         kind;
        opaque          data<AUTHDATAMAX>;
index 6ae4eeb5ebb84486e946276e2f506d14bed7f061..4a26a5c6c1e30f5099800d744d1be8b6c1caaf9d 100644 (file)
@@ -24,3 +24,6 @@ struct endpoint {
        uint32_t        Type;
        opaque          Data; /* Address and port */
 };
+
+const AUTHDATAMAX = 1024;
+const AUTHPRINTABLEMAX = 1024;
diff --git a/rpc-api/yfs_rxgk.xg b/rpc-api/yfs_rxgk.xg
new file mode 100644 (file)
index 0000000..604f416
--- /dev/null
@@ -0,0 +1,201 @@
+/* -*- c -*-
+ *
+ * Auristor/YFS fileserver and cache manager opcodes.  Please do not add or
+ * modify opcodes here without consulting Auristor, Inc..
+ *
+ * Copyright (c) 2015-2020 AuriStor, Inc. <https://www.auristor.com/>
+ * 255 W 94th St, New York NY 10025-6985 United States, Earth, Sol, Milky Way
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+package YFSRXGK_
+//prefix S
+//statindex 14
+
+//%#include <opr/time.h>
+//%#include <opr/uuid.h>
+//%#include "rxgk.h"
+//%#include "util.h"
+
+//%#ifndef DEFINED_OPR_UUID_VECTOR
+//%#define DEFINED_OPR_UUID_VECTOR 1
+//%typedef struct opr_uuid opr_uuid_vector;
+//%#endif
+
+/* The spec requires us to use PrAuthName, as defined by the Extended
+ * Authentication Names specifcation. However, in our implementation this
+ * code is in the ptserver, not in the RX layer. Directly using those
+ * symbols here would be a layering violation, so just use an idential
+ * structure with a different name - they'll be identical on the wire.
+ */
+
+const PRAUTHTYPE_GSS = 2;
+
+struct RXGK_AuthName {
+    afs_int32 kind;
+    opaque data<AUTHDATAMAX>;
+    opaque display<AUTHPRINTABLEMAX>;
+};
+
+/* Key derivation values */
+
+const YFSRXGK_SERVICE_ID               = 2508;
+const RXGK_SERVICE_ID                  = 34567;
+
+const RXGK_NONCE_SIZE                  = 20;
+
+/* Limits */
+//const RXGK_MAXENCTYPES = 255;
+//const RXGK_MAXLEVELS = 255;
+//const RXGK_MAXMIC = 1024;
+//const RXGK_MAXNONCE = 1024;
+//const RXGK_MAXDATA = 1048576;
+const RXGK_MAXAUTHENTICATOR = 1416;
+
+//typedef afs_int32 RXGK_Enctypes<RXGK_MAXENCTYPES>;
+typedef RXGK_Level RXGK_Levels<RXGK_MAXLEVELS>;
+
+//typedef opaque RXGK_Data<RXGK_MAXDATA>;
+
+#if 0
+struct RXGK_StartParams {
+       RXGK_Enctypes enctypes;
+       RXGK_Levels levels;
+       afs_int32 lifetime;
+       afs_int32 bytelife;
+       opaque client_nonce<RXGK_MAXNONCE>;
+};
+
+struct RXGK_ClientInfo {
+       afs_int32 errorcode;
+       afs_int32 enctype;
+       RXGK_Level level;
+       afs_int32 lifetime;
+       afs_int32 bytelife;
+       rxgkTime expiration;
+       opaque mic<RXGK_MAXMIC>;
+       opaque token<RXGK_MAXDATA>;
+       opaque server_nonce<RXGK_MAXNONCE>;
+};
+
+struct RXGK_CombineOptions {
+    RXGK_Enctypes enctypes;
+    RXGK_Levels levels;
+};
+
+struct RXGK_TokenInfo {
+    afs_int32 enctype;
+    RXGK_Level level;
+    afs_int32  lifetime;
+    afs_int32  bytelife;
+    rxgkTime   expirationtime;
+};
+#endif
+
+GSSNegotiate(IN RXGK_StartParams *client_start,
+            IN RXGK_Data *input_token_buffer,
+            IN RXGK_Data *opaque_in,
+            OUT RXGK_Data *output_token_buffer,
+            OUT RXGK_Data *opaque_out,
+            OUT afs_uint32 *gss_major,
+            OUT afs_uint32 *gss_minor,
+            OUT RXGK_Data *rxgk_info) = 1;
+
+CombineTokens(IN RXGK_Data *token0,
+             IN RXGK_Data *token1,
+             IN RXGK_CombineOptions *options,
+             OUT RXGK_Data *new_token,
+             OUT RXGK_TokenInfo *info) = 2;
+
+const RXGKCAPABILITIESMAX = 196;
+typedef afs_uint32 RXGKCapabilities<RXGKCAPABILITIESMAX>;
+
+GetCapabilities(
+  OUT RXGKCapabilities *caps
+) multi = 5;
+
+/**********************************************************************
+ * RX challenge and response
+ **********************************************************************/
+
+struct RXGK_Challenge {
+    opaque nonce<RXGK_NONCE_SIZE>;
+};
+
+struct RXGK_Response {
+    rxgkTime start_time;
+    opaque token<RXGK_MAXDATA>;
+    opaque authenticator<RXGK_MAXAUTHENTICATOR>;
+};
+
+struct RXGK_Authenticator {
+    opaque nonce<RXGK_NONCE_SIZE>;
+    opaque appdata<>;
+    RXGK_Level level;
+    afs_uint32 epoch;
+    afs_uint32 cid;
+    afs_int32 call_numbers<>;
+};
+
+/**********************************************************************
+ * AFS specifics
+ **********************************************************************/
+
+#if 0
+AFSCombineTokensOld(IN RXGK_Data *token0,
+                   IN RXGK_Data *token1,
+                   IN RXGK_CombineOptions *options,
+                   IN opr_uuid *destination,
+                   OUT RXGK_Data *new_token,
+                   OUT RXGK_TokenInfo *token_info) = 3;
+#endif
+
+AFSCombineTokens(IN RXGK_Data *token0,
+                IN RXGK_Data *token1,
+                IN RXGK_CombineOptions *options,
+                IN opr_uuid *destination,
+                OUT RXGK_Data *new_token,
+                OUT RXGK_TokenInfo *token_info) = 4;
+
+/* Token format */
+
+struct rxgk_key {
+       afs_uint32      enctype;
+       opaque          key<>;
+};
+
+struct RXGK_Token {
+    rxgk_key   K0;
+    RXGK_Level level;
+    rxgkTime   starttime;
+    afs_int32  lifetime;
+    afs_int32  bytelife;
+    rxgkTime   expirationtime;
+    struct RXGK_AuthName identities<>;
+};
+
+struct RXGK_TokenContainer {
+    afs_int32 kvno;
+    afs_int32 enctype;
+    opaque    encrypted_token<>;
+};
+
+#if 0
+/* Server registration (actual RPC is a generic VL one, these are
+ * just marshalling types
+ */
+
+struct RXGK_ServerKeyDataRequest {
+    afs_int32 enctypes<>;
+    opaque nonce1<>;
+};
+
+struct RXGK_ServerKeyDataResponse {
+    afs_int32 enctype;
+    opaque nonce2<>;
+};
+#endif