LCOV - code coverage report
Current view: top level - lib/crypto - gkdi.c (source / functions) Hit Total Coverage
Test: coverage report for master 70ed9daf Lines: 110 150 73.3 %
Date: 2024-01-11 09:59:51 Functions: 7 9 77.8 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Group Key Distribution Protocol functions
       4             : 
       5             :    Copyright (C) Catalyst.Net Ltd 2023
       6             : 
       7             :    This program is free software: you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation, either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <https://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include <gnutls/gnutls.h>
      23             : #include <gnutls/crypto.h>
      24             : 
      25             : #include "lib/crypto/gnutls_helpers.h"
      26             : 
      27             : #include "lib/util/bytearray.h"
      28             : 
      29             : #include "librpc/gen_ndr/ndr_security.h"
      30             : #include "librpc/gen_ndr/gkdi.h"
      31             : #include "librpc/gen_ndr/ndr_gkdi.h"
      32             : 
      33             : #include "lib/crypto/gkdi.h"
      34             : 
      35             : static const uint8_t kds_service[] = {
      36             :         /* “KDS service” as a NULL‐terminated UTF‐16LE string. */
      37             :         'K', 0, 'D', 0, 'S', 0, ' ', 0, 's', 0, 'e', 0,
      38             :         'r', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 0,   0,
      39             : };
      40             : 
      41             : struct GkdiContextShort {
      42             :         uint8_t buf[sizeof((struct GUID_ndr_buf){}.buf) + sizeof(int32_t) +
      43             :                     sizeof(int32_t) + sizeof(int32_t)];
      44             : };
      45             : 
      46         282 : static NTSTATUS make_gkdi_context(const struct GkdiDerivationCtx *ctx,
      47             :                                   struct GkdiContextShort *out_ctx)
      48             : {
      49         282 :         enum ndr_err_code ndr_err;
      50         282 :         DATA_BLOB b = {.data = out_ctx->buf, .length = sizeof out_ctx->buf};
      51             : 
      52         282 :         if (ctx->target_security_descriptor.length) {
      53           0 :                 return NT_STATUS_INVALID_PARAMETER;
      54             :         }
      55             : 
      56         282 :         ndr_err = ndr_push_struct_into_fixed_blob(
      57             :                 &b, ctx, (ndr_push_flags_fn_t)ndr_push_GkdiDerivationCtx);
      58         282 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
      59           0 :                 return ndr_map_error2ntstatus(ndr_err);
      60             :         }
      61             : 
      62         282 :         return NT_STATUS_OK;
      63             : }
      64             : 
      65          12 : static NTSTATUS make_gkdi_context_security_descriptor(
      66             :         TALLOC_CTX *mem_ctx,
      67             :         const struct GkdiDerivationCtx *ctx,
      68             :         const DATA_BLOB security_descriptor,
      69             :         DATA_BLOB *out_ctx)
      70             : {
      71          12 :         enum ndr_err_code ndr_err;
      72          12 :         struct GkdiDerivationCtx ctx_with_sd = *ctx;
      73             : 
      74          12 :         if (ctx_with_sd.target_security_descriptor.length) {
      75           0 :                 return NT_STATUS_INVALID_PARAMETER;
      76             :         }
      77             : 
      78          12 :         ctx_with_sd.target_security_descriptor = security_descriptor;
      79             : 
      80          12 :         ndr_err = ndr_push_struct_blob(out_ctx,
      81             :                                        mem_ctx,
      82             :                                        &ctx_with_sd,
      83             :                                        (ndr_push_flags_fn_t)
      84             :                                                ndr_push_GkdiDerivationCtx);
      85          12 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
      86           0 :                 return ndr_map_error2ntstatus(ndr_err);
      87             :         }
      88             : 
      89          12 :         return NT_STATUS_OK;
      90             : }
      91             : 
      92             : struct GkdiContext {
      93             :         struct GkdiDerivationCtx ctx;
      94             :         gnutls_mac_algorithm_t algorithm;
      95             : };
      96             : 
      97          13 : gnutls_mac_algorithm_t get_sp800_108_mac_algorithm(
      98             :         const struct KdfAlgorithm kdf_algorithm)
      99             : {
     100          13 :         switch (kdf_algorithm.id) {
     101          13 :         case KDF_ALGORITHM_SP800_108_CTR_HMAC:
     102          13 :                 switch (kdf_algorithm.param.sp800_108) {
     103           0 :                 case KDF_PARAM_SHA1:
     104           0 :                         return GNUTLS_MAC_SHA1;
     105           0 :                 case KDF_PARAM_SHA256:
     106           0 :                         return GNUTLS_MAC_SHA256;
     107           0 :                 case KDF_PARAM_SHA384:
     108           0 :                         return GNUTLS_MAC_SHA384;
     109           0 :                 case KDF_PARAM_SHA512:
     110           0 :                         return GNUTLS_MAC_SHA512;
     111             :                 }
     112           0 :                 break;
     113             :         }
     114             : 
     115           0 :         return GNUTLS_MAC_UNKNOWN;
     116             : }
     117             : 
     118          16 : static NTSTATUS GkdiContext(const struct ProvRootKey *const root_key,
     119             :                             struct GkdiContext *const ctx)
     120             : {
     121          16 :         NTSTATUS status = NT_STATUS_OK;
     122          16 :         gnutls_mac_algorithm_t algorithm = GNUTLS_MAC_UNKNOWN;
     123             : 
     124          16 :         if (ctx == NULL) {
     125           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     126           0 :                 goto out;
     127             :         }
     128             : 
     129          16 :         if (root_key == NULL) {
     130           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     131           0 :                 goto out;
     132             :         }
     133             : 
     134          16 :         if (root_key->version != root_key_version_1) {
     135           2 :                 status = NT_STATUS_NOT_SUPPORTED;
     136           2 :                 goto out;
     137             :         }
     138             : 
     139          14 :         if (root_key->data.length != GKDI_KEY_LEN) {
     140           1 :                 status = NT_STATUS_NOT_SUPPORTED;
     141           1 :                 goto out;
     142             :         }
     143             : 
     144          13 :         algorithm = get_sp800_108_mac_algorithm(root_key->kdf_algorithm);
     145          13 :         if (algorithm == GNUTLS_MAC_UNKNOWN) {
     146           1 :                 status = NT_STATUS_NOT_SUPPORTED;
     147           1 :                 goto out;
     148             :         }
     149             : 
     150             :         /*
     151             :          * The context comprises the GUID corresponding to the root key, the
     152             :          * GKID (which we shall initialize to zero), and the encoded target
     153             :          * security descriptor (which will initially be empty).
     154             :          */
     155          12 :         *ctx = (struct GkdiContext){
     156           0 :                 .ctx = {.guid = root_key->id,
     157             :                         .l0_idx = 0,
     158             :                         .l1_idx = 0,
     159             :                         .l2_idx = 0,
     160             :                         .target_security_descriptor = {}},
     161             :                 .algorithm = algorithm,
     162             :         };
     163          16 : out:
     164          16 :         return status;
     165             : }
     166             : 
     167          12 : static NTSTATUS compute_l1_seed_key(
     168             :         TALLOC_CTX *mem_ctx,
     169             :         struct GkdiContext *ctx,
     170             :         const DATA_BLOB security_descriptor,
     171             :         const struct ProvRootKey *const root_key,
     172             :         const struct Gkid gkid,
     173             :         uint8_t key[static const GKDI_KEY_LEN])
     174             : {
     175          12 :         NTSTATUS status = NT_STATUS_OK;
     176          12 :         struct GkdiContextShort short_ctx;
     177          12 :         int8_t n;
     178             : 
     179          12 :         ctx->ctx.l0_idx = gkid.l0_idx;
     180          12 :         ctx->ctx.l1_idx = -1;
     181          12 :         ctx->ctx.l2_idx = -1;
     182             : 
     183          12 :         status = make_gkdi_context(&ctx->ctx, &short_ctx);
     184          12 :         if (!NT_STATUS_IS_OK(status)) {
     185           0 :                 goto out;
     186             :         }
     187             : 
     188             :         /* Derive an L0 seed key with GKID = (L0, −1, −1). */
     189             : 
     190          24 :         status = samba_gnutls_sp800_108_derive_key(root_key->data.data,
     191          12 :                                                    root_key->data.length,
     192             :                                                    NULL,
     193             :                                                    0,
     194             :                                                    kds_service,
     195             :                                                    sizeof kds_service,
     196             :                                                    short_ctx.buf,
     197             :                                                    sizeof short_ctx.buf,
     198             :                                                    ctx->algorithm,
     199             :                                                    key,
     200             :                                                    GKDI_KEY_LEN);
     201          12 :         if (!NT_STATUS_IS_OK(status)) {
     202           0 :                 goto out;
     203             :         }
     204             : 
     205             :         /* Derive an L1 seed key with GKID = (L0, 31, −1). */
     206             : 
     207          12 :         ctx->ctx.l1_idx = 31;
     208             : 
     209             :         {
     210          12 :                 DATA_BLOB security_descriptor_ctx;
     211             : 
     212          12 :                 status = make_gkdi_context_security_descriptor(
     213             :                         mem_ctx,
     214           0 :                         &ctx->ctx,
     215             :                         security_descriptor,
     216             :                         &security_descriptor_ctx);
     217          12 :                 if (!NT_STATUS_IS_OK(status)) {
     218           0 :                         goto out;
     219             :                 }
     220             : 
     221          24 :                 status = samba_gnutls_sp800_108_derive_key(
     222             :                         key,
     223             :                         GKDI_KEY_LEN,
     224             :                         NULL,
     225             :                         0,
     226             :                         kds_service,
     227             :                         sizeof kds_service,
     228          12 :                         security_descriptor_ctx.data,
     229             :                         security_descriptor_ctx.length,
     230             :                         ctx->algorithm,
     231             :                         key,
     232             :                         GKDI_KEY_LEN);
     233          12 :                 data_blob_free(&security_descriptor_ctx);
     234          12 :                 if (!NT_STATUS_IS_OK(status)) {
     235           0 :                         goto out;
     236             :                 }
     237             :         }
     238             : 
     239         165 :         for (n = 30; n >= gkid.l1_idx; --n) {
     240             :                 /* Derive an L1 seed key with GKID = (L0, n, −1). */
     241             : 
     242         153 :                 ctx->ctx.l1_idx = n;
     243             : 
     244         153 :                 status = make_gkdi_context(&ctx->ctx, &short_ctx);
     245         153 :                 if (!NT_STATUS_IS_OK(status)) {
     246           0 :                         goto out;
     247             :                 }
     248             : 
     249         153 :                 status = samba_gnutls_sp800_108_derive_key(key,
     250             :                                                            GKDI_KEY_LEN,
     251             :                                                            NULL,
     252             :                                                            0,
     253             :                                                            kds_service,
     254             :                                                            sizeof kds_service,
     255             :                                                            short_ctx.buf,
     256             :                                                            sizeof short_ctx.buf,
     257             :                                                            ctx->algorithm,
     258             :                                                            key,
     259             :                                                            GKDI_KEY_LEN);
     260         153 :                 if (!NT_STATUS_IS_OK(status)) {
     261           0 :                         goto out;
     262             :                 }
     263             :         }
     264             : 
     265          12 : out:
     266          12 :         return status;
     267             : }
     268             : 
     269           6 : static NTSTATUS derive_l2_seed_key(struct GkdiContext *ctx,
     270             :                                    const struct Gkid gkid,
     271             :                                    uint8_t key[static const GKDI_KEY_LEN])
     272             : {
     273           6 :         NTSTATUS status = NT_STATUS_OK;
     274           6 :         int8_t n;
     275             : 
     276           6 :         ctx->ctx.l0_idx = gkid.l0_idx;
     277           6 :         ctx->ctx.l1_idx = gkid.l1_idx;
     278             : 
     279         123 :         for (n = 31; n >= gkid.l2_idx; --n) {
     280         117 :                 struct GkdiContextShort short_ctx;
     281             : 
     282             :                 /* Derive an L2 seed key with GKID = (L0, L1, n). */
     283             : 
     284         117 :                 ctx->ctx.l2_idx = n;
     285             : 
     286         117 :                 status = make_gkdi_context(&ctx->ctx, &short_ctx);
     287         117 :                 if (!NT_STATUS_IS_OK(status)) {
     288           0 :                         goto out;
     289             :                 }
     290             : 
     291         117 :                 status = samba_gnutls_sp800_108_derive_key(key,
     292             :                                                            GKDI_KEY_LEN,
     293             :                                                            NULL,
     294             :                                                            0,
     295             :                                                            kds_service,
     296             :                                                            sizeof kds_service,
     297             :                                                            short_ctx.buf,
     298             :                                                            sizeof short_ctx.buf,
     299             :                                                            ctx->algorithm,
     300             :                                                            key,
     301             :                                                            GKDI_KEY_LEN);
     302         117 :                 if (!NT_STATUS_IS_OK(status)) {
     303           0 :                         goto out;
     304             :                 }
     305             :         }
     306             : 
     307           6 : out:
     308           6 :         return status;
     309             : }
     310             : 
     311          18 : static enum GkidType gkid_key_type(const struct Gkid gkid)
     312             : {
     313          18 :         if (gkid.l0_idx == -1) {
     314           0 :                 return GKID_DEFAULT;
     315             :         }
     316             : 
     317          17 :         if (gkid.l1_idx == -1) {
     318           0 :                 return GKID_L0_SEED_KEY;
     319             :         }
     320             : 
     321          16 :         if (gkid.l2_idx == -1) {
     322           0 :                 return GKID_L1_SEED_KEY;
     323             :         }
     324             : 
     325           0 :         return GKID_L2_SEED_KEY;
     326             : }
     327             : 
     328          25 : static bool gkid_is_valid(const struct Gkid gkid)
     329             : {
     330          25 :         if (gkid.l0_idx < -1) {
     331           0 :                 return false;
     332             :         }
     333             : 
     334          24 :         if (gkid.l1_idx < -1 || gkid.l1_idx >= gkdi_l1_key_iteration) {
     335           0 :                 return false;
     336             :         }
     337             : 
     338          22 :         if (gkid.l2_idx < -1 || gkid.l2_idx >= gkdi_l2_key_iteration) {
     339           0 :                 return false;
     340             :         }
     341             : 
     342          20 :         if (gkid.l0_idx == -1 && gkid.l1_idx != -1) {
     343           0 :                 return false;
     344             :         }
     345             : 
     346          19 :         if (gkid.l1_idx == -1 && gkid.l2_idx != -1) {
     347           0 :                 return false;
     348             :         }
     349             : 
     350           0 :         return true;
     351             : }
     352             : 
     353          25 : NTSTATUS compute_seed_key(
     354             :         TALLOC_CTX *mem_ctx,
     355             :         const DATA_BLOB target_security_descriptor,
     356             :         const struct ProvRootKey *const root_key,
     357             :         const struct Gkid gkid,
     358             :         uint8_t key[static const GKDI_KEY_LEN])
     359             : {
     360          25 :         NTSTATUS status = NT_STATUS_OK;
     361          25 :         enum GkidType gkid_type;
     362          25 :         struct GkdiContext ctx;
     363             : 
     364          25 :         if (!gkid_is_valid(gkid)) {
     365           7 :                 status = NT_STATUS_INVALID_PARAMETER;
     366           7 :                 goto out;
     367             :         }
     368             : 
     369          18 :         gkid_type = gkid_key_type(gkid);
     370          10 :         if (gkid_type < GKID_L1_SEED_KEY) {
     371             :                 /* Don’t allow derivation of L0 seed keys. */
     372           2 :                 status = NT_STATUS_INVALID_PARAMETER;
     373           2 :                 goto out;
     374             :         }
     375             : 
     376          16 :         status = GkdiContext(root_key, &ctx);
     377          16 :         if (!NT_STATUS_IS_OK(status)) {
     378           4 :                 goto out;
     379             :         }
     380             : 
     381          12 :         status = compute_l1_seed_key(
     382             :                 mem_ctx, &ctx, target_security_descriptor, root_key, gkid, key);
     383          12 :         if (!NT_STATUS_IS_OK(status)) {
     384           0 :                 goto out;
     385             :         }
     386             : 
     387          12 :         if (gkid_type == GKID_L2_SEED_KEY) {
     388           6 :                 status = derive_l2_seed_key(&ctx, gkid, key);
     389           6 :                 if (!NT_STATUS_IS_OK(status)) {
     390           0 :                         goto out;
     391             :                 }
     392             :         }
     393             : 
     394          12 : out:
     395          25 :         return status;
     396             : }

Generated by: LCOV version 1.14