]> www.infradead.org Git - users/dhowells/kafs-utils.git/commitdiff
Add gssapi test program
authorDavid Howells <dhowells@redhat.com>
Wed, 30 Sep 2020 08:22:46 +0000 (09:22 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 5 May 2023 10:53:26 +0000 (11:53 +0100)
kafs/Makefile
kafs/gssapi_test.C [new file with mode: 0644]
rpc-api/add_key.xg

index 13fa6077a36fb29ffd0454a04a535e6936c04419..b543b435a35223cc0ffb3778e0afbb036bd2ff03 100644 (file)
@@ -29,7 +29,8 @@ FS_SRCS := \
 GSSAPI_SRCS := \
        gssapi.C \
        gssapi_aklog.C \
-       gssapi_help.C
+       gssapi_help.C \
+       gssapi_test.C
 
 PTS_SRCS := \
        pts.C \
@@ -94,7 +95,7 @@ LIBDIR        := ../lib
 
 kafs: $(KAFS_OBJS) $(LIBDIR)/libkafs_utils.a
        $(CXX) -o $@ $(KAFS_OBJS) -L$(LIBDIR) -lkafs_utils -lkafs_client -luuid -lfmt \
-               -lgssapi_krb5 -lkeyutils
+               -lgssapi_krb5 -lk5crypto -lkrb5 -lkeyutils
 
 clean:
        $(RM) *~ *.o $(DEPS)
diff --git a/kafs/gssapi_test.C b/kafs/gssapi_test.C
new file mode 100644 (file)
index 0000000..76f1ed2
--- /dev/null
@@ -0,0 +1,364 @@
+/* gssapi_test.C: gssapi test client
+ *
+ * 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"
+#include "arg_parse.H"
+
+#define TEST_PORT              7009
+#define RX_PERF_SERVICE                147
+
+using rxrpc::ref;
+using kafs::afs::RXGK_StartParams;
+using kafs::afs::RXGK_ClientInfo;
+using kafs::afs::RXGK_AuthName;
+using kafs::afs::RXGK_Token;
+using kafs::afs::RXGK_TokenContainer;
+
+static bool debug_gss = false;
+
+#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)
+
+class KRB5_context {
+       krb5_context    ctx;
+public:
+       KRB5_context() {
+               ctx = NULL;
+               int ec = krb5_init_context(&ctx);
+               if (ec)
+                       throw std::runtime_error(fmt::format("Kerberos error {:d}", ec));
+       }
+       ~KRB5_context() { krb5_free_context(ctx); }
+       operator krb5_context() { return ctx; }
+};
+
+static std::string KRB5_error(KRB5_context &krb, krb5_error_code ec)
+{
+       const char *p = krb5_get_error_message(krb, ec);
+       std::string msg(fmt::format("Kerberos error: {}", p));
+       krb5_free_error_message(krb, p);
+       return msg;
+}
+
+
+/*
+ * Forge and encrypt a ticket.
+ *
+ * struct rxgk_key {
+ *     afs_uint32      enctype;
+ *     opaque          key<>;
+ * };
+ *
+ * struct RXGK_AuthName {
+ *     afs_int32       kind;
+ *     opaque          data<AUTHDATAMAX>;
+ *     opaque          display<AUTHPRINTABLEMAX>;
+ * };
+ *
+ * struct RXGK_Token {
+ *     rxgk_key                K0;
+ *     RXGK_Level              level;
+ *     rxgkTime                starttime;
+ *     afs_int32               lifetime;
+ *     afs_int32               bytelife;
+ *     rxgkTime                expirationtime;
+ *     struct RXGK_AuthName    identities<>;
+ * };
+ */
+static void forge_rxgk_ticket(kafs::Context *ctx,
+                             RXGK_ClientInfo &client_info,
+                             rxrpc::Opaque &ticket_key,
+                             rxrpc::Opaque &K0,
+                             int tenctype,
+                             int kvno,
+                             rxrpc::Opaque &ticket)
+{
+       krb5_error_code ec;
+       RXGK_TokenContainer container;
+       RXGK_Token token;
+       size_t enc_size;
+
+       debug_gss("Ticket enctype {}\n", tenctype);
+       debug_gss("Session enctype {}\n", client_info.enctype);
+
+       container.kvno          = kvno;
+       container.enctype       = tenctype;
+
+       token.K0.enctype        = client_info.enctype;
+       token.K0.key            = K0;
+       token.level             = client_info.level;
+       token.starttime         = 0;
+       token.lifetime          = client_info.lifetime;
+       token.bytelife          = client_info.bytelife;
+       token.expirationtime    = client_info.expiration;
+
+       token.identities.resize(1);
+       token.identities[0].kind = kafs::afs::PRAUTHTYPE_GSS;
+       token.identities[0].data.buffer_size = 4;
+       token.identities[0].data.buffer = (void *)"test";
+       token.identities[0].display.buffer_size = 5;
+       token.identities[0].display.buffer = (void *)"TEST!";
+
+       rxrpc::Resv resv = { .max_ioc = 1, .flat = true };
+       kafs::afs::xdr::reserve_RXGK_Token(resv, token);
+       ref<rxrpc::Enc_buffer> buf = alloc_enc_buffer(resv);
+       kafs::afs::xdr::encode_RXGK_Token(buf, token);
+       rxrpc::seal_buffer(buf);
+
+       KRB5_context krb;
+
+       ec = krb5_c_encrypt_length(krb, tenctype, buf->size, &enc_size);
+       if (ec)
+               throw std::runtime_error(KRB5_error(krb, ec));
+
+       container.encrypted_token.reserve(enc_size);
+
+       krb5_keyblock keyblock;
+       keyblock.magic          = KV5M_KEYBLOCK;
+       keyblock.enctype        = tenctype;
+       keyblock.length         = ticket_key.size();
+       keyblock.contents       = (krb5_octet *)ticket_key.buffer;
+
+       debug_gss("Keyblock e={} l={}\n", keyblock.enctype, keyblock.length);
+
+       krb5_data unenc;
+       unenc.magic             = KV5M_DATA;
+       unenc.length            = buf->size;
+       unenc.data              = (char *)buf->enc_buf;
+
+       debug_gss("Unenc l={}\n", unenc.length);
+
+       krb5_enc_data enc;
+       enc.magic               = KV5M_ENC_DATA;
+       enc.enctype             = tenctype;
+       enc.kvno                = kvno;
+       enc.ciphertext.magic    = KV5M_DATA;
+       enc.ciphertext.length   = enc_size;
+       enc.ciphertext.data     = (char *)container.encrypted_token.buffer;
+
+       debug_gss("Enc e={} k={} l={}\n", enc.enctype, enc.kvno, enc.ciphertext.length);
+
+       ec = krb5_c_encrypt(krb, &keyblock, kafs::afs::RXGK_SERVER_ENC_TOKEN,
+                           NULL, &unenc, &enc);
+       if (ec)
+               throw std::runtime_error(KRB5_error(krb, ec));
+       container.encrypted_token.buffer_size = enc.ciphertext.length;
+
+       rxrpc::Resv resv2 = { .max_ioc = 1, .flat = true };
+       kafs::afs::xdr::reserve_RXGK_TokenContainer(resv2, container);
+       ref<rxrpc::Enc_buffer> buf2 = alloc_enc_buffer(resv2);
+       kafs::afs::xdr::encode_RXGK_TokenContainer(buf2, container);
+       rxrpc::seal_buffer(buf2);
+
+       ticket.reserve(buf2->size);
+       ticket.buffer_size = buf2->size;
+       memcpy(ticket.buffer, buf2->enc_buf, ticket.buffer_size);
+       ticket.del_buffer = true;
+}
+
+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("test@{}", 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 test - GSSAPI tester
+ * ARG: "-server <server name>"
+ * ARG: "[-cell <cell name>]"
+ * ARG: "[-enctype <enc>]"
+ * ARG: "[-level <level>]"
+ * ARG: "[-tenctype <enc>]"
+ * ARG: "[-kvno <kvno>]"
+ * ARG: "[-verbose]"
+ * ARG: "[-encrypt]"                           - Auth
+ *
+ * Forge a ticket and poke the server by RxGK.
+ */
+void COMMAND_gssapi_test(
+       kafs::Context                   *ctx,
+       kafs::Volserver_spec            &a_server,
+       std::string                     &a_enctype,
+       std::string                     &a_level,
+       std::string                     &a_tenctype,
+       std::string                     &a_kvno,
+       bool                            a_verbose)
+{
+       rxrpc::security_auth_level rxlevel = rxrpc::security_encrypt;
+       RXGK_ClientInfo client_info;
+       rxrpc::Opaque ticket_key, K0;
+       unsigned int kvno = 1;
+       int tenctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+       int tklen, k0len, i;
+
+       client_info.expiration  = 0;
+       client_info.level       = kafs::afs::RXGK_LEVEL_CRYPT;
+       client_info.lifetime    = 0;
+       client_info.bytelife    = 0;
+       client_info.enctype     = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+
+       if (a_enctype.size())
+               client_info.enctype = stoi(a_enctype);
+
+       switch (client_info.enctype) {
+       case ENCTYPE_AES128_CTS_HMAC_SHA1_96:           k0len = 16; break;
+       case ENCTYPE_AES256_CTS_HMAC_SHA1_96:           k0len = 32; break;
+       case ENCTYPE_AES128_CTS_HMAC_SHA256_128:        k0len = 16; break;
+       case ENCTYPE_AES256_CTS_HMAC_SHA384_192:        k0len = 32; break;
+       case ENCTYPE_CAMELLIA128_CTS_CMAC:              k0len = 16; break;
+       case ENCTYPE_CAMELLIA256_CTS_CMAC:              k0len = 32; break;
+       default:
+               throw std::runtime_error(
+                       fmt::format("GSSAPI Unknown enctype {}", client_info.enctype));
+       }
+
+       K0.reserve(k0len);
+       for (i = 0; i < k0len; i++)
+               ((unsigned char *)K0.buffer)[i] = i;
+       K0.buffer_size = k0len;
+
+       if (a_level.size()) {
+               if (a_level == "a") {
+                       client_info.level = kafs::afs::RXGK_LEVEL_CLEAR;
+                       rxlevel = rxrpc::security_clear;
+               } else if (a_level == "i") {
+                       client_info.level = kafs::afs::RXGK_LEVEL_AUTH;
+                       rxlevel = rxrpc::security_integrity_only;
+               } else if (a_level == "c") {
+                       client_info.level = kafs::afs::RXGK_LEVEL_CRYPT;
+                       rxlevel = rxrpc::security_encrypt;
+               } else {
+                       throw std::invalid_argument("Unknown level");
+               }
+       }
+
+       if (a_tenctype.size())
+               tenctype = stoi(a_tenctype);
+
+       switch (tenctype) {
+       case ENCTYPE_AES128_CTS_HMAC_SHA1_96:           tklen = 16; break;
+       case ENCTYPE_AES256_CTS_HMAC_SHA1_96:           tklen = 32; break;
+       case ENCTYPE_AES128_CTS_HMAC_SHA256_128:        tklen = 16; break;
+       case ENCTYPE_AES256_CTS_HMAC_SHA384_192:        tklen = 32; break;
+       case ENCTYPE_CAMELLIA128_CTS_CMAC:              tklen = 16; break;
+       case ENCTYPE_CAMELLIA256_CTS_CMAC:              tklen = 32; break;
+       default:
+               throw std::runtime_error(
+                       fmt::format("GSSAPI Unknown enctype {}", tenctype));
+       }
+
+       ticket_key.reserve(tklen);
+       for (i = 0; i < tklen; i++)
+               ((unsigned char *)ticket_key.buffer)[i] = i;
+       ticket_key.buffer_size = tklen;
+
+       if (a_kvno.size())
+               kvno = stoi(a_kvno);
+
+       forge_rxgk_ticket(ctx, client_info, ticket_key, K0, tenctype, kvno, client_info.token);
+       rxgk_add_key(ctx, client_info, K0, a_verbose, kafs::afs::AFSTOKEN_UNION_YFSGK);
+
+       rxrpc::find_transport();
+
+       ctx->security = rxrpc::new_security(fmt::format("test@{}", ctx->cell_name), rxlevel);
+
+       kafs::open_endpoint(ctx);
+
+       rxrpc::Call_params params;
+       params.endpoint = ctx->endpoint;
+       
+       ref<kafs::FS_site> site = kafs::resolve_server_spec(ctx, a_server);
+       params.peer = site->vs_addrs[0];
+       params.peer.srx_service = RX_PERF_SERVICE;
+       if (params.peer.transport.family == AF_INET)
+               params.peer.transport.sin.sin_port = htons(TEST_PORT);
+       else
+               params.peer.transport.sin6.sin6_port = htons(TEST_PORT);
+       params.peer_len = sizeof(params.peer);
+
+       std::vector<unsigned int> caps;
+       kafs::afs::RXAFS::GetCapabilities(&params, caps);
+       fmt::print("Success!");
+       for (size_t i = 0; i < caps.size(); i++)
+               fmt::print(" {:08x}", caps[i]);
+       fmt::print("\n");
+}
index 5290839a2362950d1e5e23541b35de03e5048d27..508bbe02b2d1c36eab520f0c41f19c1556363fcc 100644 (file)
@@ -8,6 +8,7 @@
 typedef int64_t opr_time;
 
 const AFSTOKEN_RK_TIX_MAX = 12000;     /* Matches entry in rxkad.h */
+const AFSTOKEN_GK_TOK_MAX = 1048576;   /* Matches RXGK_MAXDATA in rxgk_int.xg */
 
 struct token_rxkad {
     afs_int32 viceid;
@@ -19,6 +20,17 @@ struct token_rxkad {
     opaque ticket<AFSTOKEN_RK_TIX_MAX>;
 };
 
+struct token_rxgk {
+       afs_int64       gk_viceid;
+       afs_int32       gk_enctype;
+       afs_int32       gk_level;
+       afs_uint32      gk_lifetime;
+       afs_uint32      gk_bytelife;
+       afs_int64       gk_expiration;
+       opaque          gk_token<AFSTOKEN_GK_TOK_MAX>;
+       opaque          gk_k0<AFSTOKEN_GK_TOK_MAX>;
+};
+
 struct token_yfs_rxgk {
     opr_time begintime;
     opr_time endtime;
@@ -32,11 +44,14 @@ struct token_yfs_rxgk {
 
 const AFSTOKEN_UNION_NOAUTH = 0;
 const AFSTOKEN_UNION_KAD = 2;
+const AFSTOKEN_UNION_GK = 4;
 const AFSTOKEN_UNION_YFSGK = 6;
 
 union ktc_tokenUnion switch (afs_int32 type) {
     case AFSTOKEN_UNION_KAD:
        token_rxkad     kad;
+    case AFSTOKEN_UNION_GK:
+       token_rxgk      gk;
     case AFSTOKEN_UNION_YFSGK:
        token_yfs_rxgk  ygk;
 };