LCOV - code coverage report
Current view: top level - auth/kerberos - gssapi_pac.c (source / functions) Hit Total Coverage
Test: coverage report for master 469b22b8 Lines: 86 123 69.9 %
Date: 2024-06-10 12:05:21 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    kerberos authorization data (PAC) utility library
       4             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2011
       5             :    Copyright (C) Simo Sorce 2010.
       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 <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : 
      23             : #undef DBGC_CLASS
      24             : #define DBGC_CLASS DBGC_AUTH
      25             : 
      26             : #ifdef HAVE_KRB5
      27             : 
      28             : #include "auth/kerberos/pac_utils.h"
      29             : 
      30             : #if 0
      31             : /* FIXME - need proper configure/waf test
      32             :  * to determine if gss_mech_krb5 and friends
      33             :  * exist. JRA.
      34             :  */
      35             : /*
      36             :  * These are not exported by Solaris -lkrb5
      37             :  * Maybe move to libreplace somewhere?
      38             :  */
      39             : static const gss_OID_desc krb5_gss_oid_array[] = {
      40             :         /* this is the official, rfc-specified OID */
      41             :         { 9, "\052\206\110\206\367\022\001\002\002" },
      42             :         /* this is the pre-RFC mech OID */
      43             :         { 5, "\053\005\001\005\002" },
      44             :         /* this is the unofficial, incorrect mech OID emitted by MS */
      45             :         { 9, "\052\206\110\202\367\022\001\002\002" },
      46             :         { 0, 0 }
      47             : };
      48             : 
      49             : const gss_OID_desc * const gss_mech_krb5              = krb5_gss_oid_array+0;
      50             : const gss_OID_desc * const gss_mech_krb5_old          = krb5_gss_oid_array+1;
      51             : const gss_OID_desc * const gss_mech_krb5_wrong        = krb5_gss_oid_array+2;
      52             : #endif
      53             : 
      54             : #ifndef GSS_KRB5_INQ_SSPI_SESSION_KEY_OID
      55             : #define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11
      56             : #define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"
      57             : #endif
      58             : 
      59             : gss_OID_desc gse_sesskey_inq_oid = {
      60             :         GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH,
      61             :         discard_const(GSS_KRB5_INQ_SSPI_SESSION_KEY_OID)
      62             : };
      63             : 
      64             : #ifndef GSS_KRB5_SESSION_KEY_ENCTYPE_OID
      65             : #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10
      66             : #define GSS_KRB5_SESSION_KEY_ENCTYPE_OID  "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04"
      67             : #endif
      68             : 
      69             : gss_OID_desc gse_sesskeytype_oid = {
      70             :         GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH,
      71             :         discard_const(GSS_KRB5_SESSION_KEY_ENCTYPE_OID)
      72             : };
      73             : 
      74             : /* The Heimdal OID for getting the PAC */
      75             : #define EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH 8
      76             : /*                                                      EXTRACTION OID             AUTHZ ID */
      77             : #define EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID "\x2a\x85\x70\x2b\x0d\x03" "\x81\x00"
      78             : 
      79       32112 : NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx,
      80             :                                 gss_ctx_id_t gssapi_context,
      81             :                                 gss_name_t gss_client_name,
      82             :                                 DATA_BLOB *pac_blob)
      83             : {
      84         888 :         NTSTATUS status;
      85         888 :         OM_uint32 gss_maj, gss_min;
      86             : #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE
      87             : /*
      88             :  * gss_get_name_attribute() in MIT krb5 1.10.0 can return uninitialized pac_display_buffer
      89             :  * and later gss_release_buffer() will crash on attempting to release it.
      90             :  *
      91             :  * So always initialize the buffer descriptors.
      92             :  *
      93             :  * See following links for more details:
      94             :  * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=658514
      95             :  * http://krbdev.mit.edu/rt/Ticket/Display.html?user=guest&pass=guest&id=7087
      96             :  */
      97        8279 :         gss_buffer_desc pac_buffer = {
      98             :                 .value = NULL,
      99             :                 .length = 0
     100             :         };
     101        8279 :         gss_buffer_desc pac_display_buffer = {
     102             :                 .value = NULL,
     103             :                 .length = 0
     104             :         };
     105        8279 :         gss_buffer_desc pac_name = {
     106             :                 .value = discard_const("urn:mspac:"),
     107             :                 .length = sizeof("urn:mspac:")-1
     108             :         };
     109        8279 :         int more = -1;
     110        8279 :         int authenticated = false;
     111        8279 :         int complete = false;
     112             : 
     113        8279 :         gss_maj = gss_get_name_attribute(
     114             :                 &gss_min, gss_client_name, &pac_name,
     115             :                 &authenticated, &complete,
     116             :                 &pac_buffer, &pac_display_buffer, &more);
     117             : 
     118        8279 :         if (gss_maj != 0) {
     119           5 :                 gss_OID oid = discard_const(gss_mech_krb5);
     120           5 :                 DBG_NOTICE("obtaining PAC via GSSAPI gss_get_name_attribute "
     121             :                            "failed: %s\n", gssapi_error_string(mem_ctx,
     122             :                                                                gss_maj, gss_min,
     123             :                                                                oid));
     124           5 :                 return NT_STATUS_ACCESS_DENIED;
     125        8274 :         } else if (authenticated && complete) {
     126             :                 /* The PAC blob is returned directly */
     127        8274 :                 *pac_blob = data_blob_talloc(mem_ctx, pac_buffer.value,
     128             :                                             pac_buffer.length);
     129             : 
     130        8274 :                 if (!pac_blob->data) {
     131           0 :                         status = NT_STATUS_NO_MEMORY;
     132             :                 } else {
     133        8274 :                         status = NT_STATUS_OK;
     134             :                 }
     135             : 
     136        8274 :                 gss_release_buffer(&gss_min, &pac_buffer);
     137        8274 :                 gss_release_buffer(&gss_min, &pac_display_buffer);
     138        8274 :                 return status;
     139             :         } else {
     140           0 :                 DEBUG(0, ("obtaining PAC via GSSAPI failed: authenticated: %s, complete: %s, more: %s\n",
     141             :                           authenticated ? "true" : "false",
     142             :                           complete ? "true" : "false",
     143             :                           more ? "true" : "false"));
     144           0 :                 return NT_STATUS_ACCESS_DENIED;
     145             :         }
     146             : 
     147             : #elif defined(HAVE_GSS_INQUIRE_SEC_CONTEXT_BY_OID)
     148       23833 :         gss_OID_desc pac_data_oid = {
     149             :                 .elements = discard_const(EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID),
     150             :                 .length = EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH
     151             :         };
     152             : 
     153       23833 :         gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
     154             : 
     155             :         /* If we didn't have the routine to get a verified, validated
     156             :          * PAC (supplied only by MIT at the time of writing), then try
     157             :          * with the Heimdal OID (fetches the PAC directly and always
     158             :          * validates) */
     159       23833 :         gss_maj = gss_inquire_sec_context_by_oid(
     160             :                                 &gss_min, gssapi_context,
     161             :                                 &pac_data_oid, &set);
     162             : 
     163             :         /* First check for the error MIT gives for an unknown OID */
     164       23833 :         if (gss_maj == GSS_S_UNAVAILABLE) {
     165           0 :                 DEBUG(1, ("unable to obtain a PAC against this GSSAPI library.  "
     166             :                           "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n"));
     167       23833 :         } else if (gss_maj != 0) {
     168           8 :                 DEBUG(2, ("obtaining PAC via GSSAPI gss_inquire_sec_context_by_oid (Heimdal OID) failed: %s\n",
     169             :                           gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5)));
     170             :         } else {
     171       23825 :                 if (set == GSS_C_NO_BUFFER_SET) {
     172           0 :                         DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown "
     173             :                                   "data in results.\n"));
     174           0 :                         return NT_STATUS_INTERNAL_ERROR;
     175             :                 }
     176             : 
     177             :                 /* The PAC blob is returned directly */
     178       23825 :                 *pac_blob = data_blob_talloc(mem_ctx, set->elements[0].value,
     179             :                                             set->elements[0].length);
     180       23825 :                 if (!pac_blob->data) {
     181           0 :                         status = NT_STATUS_NO_MEMORY;
     182             :                 } else {
     183       23825 :                         status = NT_STATUS_OK;
     184             :                 }
     185             : 
     186       23825 :                 gss_maj = gss_release_buffer_set(&gss_min, &set);
     187       23825 :                 return status;
     188             :         }
     189             : #else
     190             :         DEBUG(1, ("unable to obtain a PAC against this GSSAPI library.  "
     191             :                   "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n"));
     192             : #endif
     193           8 :         return NT_STATUS_ACCESS_DENIED;
     194             : }
     195             : 
     196       87209 : NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx,
     197             :                                 gss_ctx_id_t gssapi_context,
     198             :                                 DATA_BLOB *session_key, 
     199             :                                 uint32_t *keytype)
     200             : {
     201        2531 :         OM_uint32 gss_min, gss_maj;
     202       87209 :         gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
     203             : 
     204       87209 :         gss_maj = gss_inquire_sec_context_by_oid(
     205             :                                 &gss_min, gssapi_context,
     206             :                                 &gse_sesskey_inq_oid, &set);
     207       87209 :         if (gss_maj) {
     208           0 :                 DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n",
     209             :                           gssapi_error_string(mem_ctx,
     210             :                                               gss_maj,
     211             :                                               gss_min,
     212             :                                               discard_const_p(struct gss_OID_desc_struct,
     213             :                                                               gss_mech_krb5))));
     214           0 :                 return NT_STATUS_NO_USER_SESSION_KEY;
     215             :         }
     216             : 
     217       87209 :         if ((set == GSS_C_NO_BUFFER_SET) ||
     218       87209 :             (set->count == 0)) {
     219             : #ifdef HAVE_GSSKRB5_GET_SUBKEY
     220           0 :                 krb5_keyblock *subkey;
     221           0 :                 gss_maj = gsskrb5_get_subkey(&gss_min,
     222             :                                              gssapi_context,
     223             :                                              &subkey);
     224           0 :                 if (gss_maj != 0) {
     225           0 :                         DEBUG(1, ("NO session key for this mech\n"));
     226           0 :                         return NT_STATUS_NO_USER_SESSION_KEY;
     227             :                 }
     228           0 :                 if (session_key) {
     229           0 :                         *session_key = data_blob_talloc(mem_ctx,
     230             :                                                         KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
     231             :                 }
     232           0 :                 if (keytype) {
     233           0 :                         *keytype = KRB5_KEY_TYPE(subkey);
     234             :                 }
     235           0 :                 krb5_free_keyblock(NULL /* should be krb5_context */, subkey);
     236           0 :                 return NT_STATUS_OK;
     237             : #else
     238           0 :                 DEBUG(0, ("gss_inquire_sec_context_by_oid didn't return any session key (and no alternative method available)\n"));
     239           0 :                 return NT_STATUS_NO_USER_SESSION_KEY;
     240             : #endif
     241             :         }
     242             : 
     243       87209 :         if (session_key) {
     244       48873 :                 *session_key = data_blob_talloc(mem_ctx, set->elements[0].value,
     245             :                                                 set->elements[0].length);
     246             :         }
     247             : 
     248       87209 :         if (keytype) {
     249         985 :                 int diflen, i;
     250         985 :                 const uint8_t *p;
     251             : 
     252       38336 :                 *keytype = 0;
     253       38336 :                 if (set->count < 2) {
     254             : 
     255             : #ifdef HAVE_GSSKRB5_GET_SUBKEY
     256         985 :                         krb5_keyblock *subkey;
     257       29493 :                         gss_maj = gsskrb5_get_subkey(&gss_min,
     258             :                                                      gssapi_context,
     259             :                                                      &subkey);
     260       29493 :                         if (gss_maj == 0) {
     261       29493 :                                 *keytype = KRB5_KEY_TYPE(subkey);
     262       29493 :                                 krb5_free_keyblock(NULL /* should be krb5_context */, subkey);
     263             :                         }
     264             : #endif
     265       29493 :                         gss_release_buffer_set(&gss_min, &set);
     266             :         
     267       29493 :                         return NT_STATUS_OK;
     268             : 
     269        8843 :                 } else if (memcmp(set->elements[1].value,
     270        8843 :                                   gse_sesskeytype_oid.elements,
     271        8843 :                                   gse_sesskeytype_oid.length) != 0) {
     272             :                         /* Perhaps a non-krb5 session key */
     273           0 :                         gss_release_buffer_set(&gss_min, &set);
     274           0 :                         return NT_STATUS_OK;
     275             :                 }
     276        8843 :                 p = (const uint8_t *)set->elements[1].value + gse_sesskeytype_oid.length;
     277        8843 :                 diflen = set->elements[1].length - gse_sesskeytype_oid.length;
     278        8843 :                 if (diflen <= 0) {
     279           0 :                         gss_release_buffer_set(&gss_min, &set);
     280           0 :                         return NT_STATUS_INVALID_PARAMETER;
     281             :                 }
     282       17686 :                 for (i = 0; i < diflen; i++) {
     283        8843 :                         *keytype = (*keytype << 7) | (p[i] & 0x7f);
     284        8843 :                         if (i + 1 != diflen && (p[i] & 0x80) == 0) {
     285           0 :                                 gss_release_buffer_set(&gss_min, &set);
     286           0 :                                 return NT_STATUS_INVALID_PARAMETER;
     287             :                         }
     288             :                 }
     289             :         }
     290             : 
     291       57716 :         gss_release_buffer_set(&gss_min, &set);
     292       57716 :         return NT_STATUS_OK;
     293             : }
     294             : 
     295             : 
     296          31 : char *gssapi_error_string(TALLOC_CTX *mem_ctx,
     297             :                           OM_uint32 maj_stat, OM_uint32 min_stat,
     298             :                           const gss_OID mech)
     299             : {
     300           0 :         OM_uint32 disp_min_stat, disp_maj_stat;
     301           0 :         gss_buffer_desc maj_error_message;
     302           0 :         gss_buffer_desc min_error_message;
     303           0 :         char *maj_error_string, *min_error_string;
     304          31 :         OM_uint32 msg_ctx = 0;
     305             : 
     306           0 :         char *ret;
     307             : 
     308          31 :         maj_error_message.value = NULL;
     309          31 :         min_error_message.value = NULL;
     310          31 :         maj_error_message.length = 0;
     311          31 :         min_error_message.length = 0;
     312             : 
     313          31 :         disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat,
     314             :                                            GSS_C_GSS_CODE, mech,
     315             :                                            &msg_ctx, &maj_error_message);
     316          31 :         if (disp_maj_stat != 0) {
     317           0 :                 maj_error_message.value = NULL;
     318           0 :                 maj_error_message.length = 0;
     319             :         }
     320          31 :         disp_maj_stat = gss_display_status(&disp_min_stat, min_stat,
     321             :                                            GSS_C_MECH_CODE, mech,
     322             :                                            &msg_ctx, &min_error_message);
     323          31 :         if (disp_maj_stat != 0) {
     324           0 :                 min_error_message.value = NULL;
     325           0 :                 min_error_message.length = 0;
     326             :         }
     327             : 
     328          31 :         maj_error_string = talloc_strndup(mem_ctx,
     329          31 :                                           (char *)maj_error_message.value,
     330             :                                           maj_error_message.length);
     331             : 
     332          31 :         min_error_string = talloc_strndup(mem_ctx,
     333          31 :                                           (char *)min_error_message.value,
     334             :                                           min_error_message.length);
     335             : 
     336          31 :         ret = talloc_asprintf(mem_ctx, "%s: %s",
     337             :                                 maj_error_string, min_error_string);
     338             : 
     339          31 :         talloc_free(maj_error_string);
     340          31 :         talloc_free(min_error_string);
     341             : 
     342          31 :         gss_release_buffer(&disp_min_stat, &maj_error_message);
     343          31 :         gss_release_buffer(&disp_min_stat, &min_error_message);
     344             : 
     345          31 :         return ret;
     346             : }
     347             : 
     348             : #endif /* HAVE_KRB5 */

Generated by: LCOV version 1.14