components/krb5/patches/073-root-init-cred-kt.patch
changeset 6867 87f7fd05f888
child 6978 14cbeb78966a
equal deleted inserted replaced
6866:4c1935f5ec9a 6867:87f7fd05f888
       
     1 #
       
     2 # This patch provides support in kerberos for root acquiring a default cred via
       
     3 # either a root, host service principal or sam account name keys in the keytab
       
     4 # if root doesn't have a cred already.  Note that if root has a client keytab
       
     5 # provisioned then that will be used instead.
       
     6 #
       
     7 # This is Solaris specific behavior that MIT will not take upstream.
       
     8 # Patch source: in-house
       
     9 #
       
    10 
       
    11 --- a/src/lib/gssapi/krb5/acquire_cred.c
       
    12 +++ b/src/lib/gssapi/krb5/acquire_cred.c
       
    13 @@ -77,6 +77,7 @@
       
    14  #else
       
    15  #include <strings.h>
       
    16  #endif
       
    17 +#include <ctype.h>
       
    18  
       
    19  #ifdef USE_LEASH
       
    20  #ifdef _WIN64
       
    21 @@ -88,6 +89,9 @@
       
    22  static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
       
    23  #endif
       
    24  
       
    25 +/* for solaris root fallback check */
       
    26 +static char defktname[BUFSIZ];
       
    27 +
       
    28  #ifndef LEAN_CLIENT
       
    29  k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
       
    30  static char *krb5_gss_keytab = NULL;
       
    31 @@ -590,6 +594,151 @@
       
    32      set_refresh_time(context, cred->ccache, refresh);
       
    33  }
       
    34  
       
    35 +#define	SAM_ACCOUNT_LEN 17 /* 15:hostname + 1:$ + 1:\0 */
       
    36 +krb5_error_code
       
    37 +get_sam_account_name(char **name)
       
    38 +{
       
    39 +    char *p, localname[SAM_ACCOUNT_LEN];
       
    40 +
       
    41 +    if (name == NULL)
       
    42 +	return (EINVAL);
       
    43 +
       
    44 +    if (gethostname(localname, SAM_ACCOUNT_LEN) != 0)
       
    45 +	return (errno);
       
    46 +
       
    47 +    localname[SAM_ACCOUNT_LEN - 2] = '\0';
       
    48 +
       
    49 +    if ((p = strchr(localname, '.')) != NULL)
       
    50 +	*p = '\0';
       
    51 +
       
    52 +    for (p = localname; *p; p++)
       
    53 +	*p = toupper(*p);
       
    54 +
       
    55 +    (void) strlcat(localname, "$", SAM_ACCOUNT_LEN);
       
    56 +
       
    57 +    *name = strdup(localname);
       
    58 +    if (*name == NULL)
       
    59 +	return (ENOMEM);
       
    60 +
       
    61 +    return (0);
       
    62 +}
       
    63 +
       
    64 +krb5_error_code krb5_kt_find_realm(krb5_context, krb5_keytab, krb5_principal,
       
    65 +                                   krb5_data *);
       
    66 +
       
    67 +static krb5_error_code
       
    68 +get_root_initcred_keytab(krb5_context context,
       
    69 +                      krb5_creds *kcreds,
       
    70 +                      krb5_gss_cred_id_rec *gsscred,
       
    71 +                      const char *name,
       
    72 +                      krb5_int32 type,
       
    73 +                      krb5_get_init_creds_opt *opt)
       
    74 +{
       
    75 +    krb5_principal client_princ;
       
    76 +    krb5_error_code code;
       
    77 +
       
    78 +    if (type == KRB5_NT_SRV_HST) {
       
    79 +        code = krb5_sname_to_principal(context, NULL, name, type,
       
    80 +                                       &client_princ);
       
    81 +    } else {
       
    82 +        /* Assuming KRB5_NT_PRINCIPAL */
       
    83 +        code = krb5_parse_name(context, name, &client_princ);
       
    84 +    }
       
    85 +    if (code)
       
    86 +        return code;
       
    87 +
       
    88 +    if (krb5_is_referral_realm(&client_princ->realm)) {
       
    89 +        krb5_data realm;
       
    90 +
       
    91 +        code = krb5_kt_find_realm(context, gsscred->client_keytab,
       
    92 +                                  client_princ, &realm);
       
    93 +        if (code == 0) {
       
    94 +            krb5_free_data_contents(context, &client_princ->realm);
       
    95 +            client_princ->realm.length = realm.length;
       
    96 +            client_princ->realm.data = realm.data;
       
    97 +        } else {
       
    98 +            /* Try to set a useful error message */
       
    99 +            char *princ_name = NULL;
       
   100 +            char kt_name[BUFSIZ];
       
   101 +
       
   102 +            (void) krb5_unparse_name(context, client_princ, &princ_name);
       
   103 +            (void) krb5_kt_get_name(context, gsscred->client_keytab, kt_name,
       
   104 +                                    BUFSIZ);
       
   105 +            krb5_set_error_message(context, code,
       
   106 +                                   _("Failed to find realm for %s in "
       
   107 +                                     "keytab %s"),
       
   108 +                                   princ_name != NULL ? princ_name : "unknown",
       
   109 +                                   kt_name);
       
   110 +            krb5_free_unparsed_name(context, princ_name);
       
   111 +        }
       
   112 +    }
       
   113 +    if (code)
       
   114 +        goto cleanup;
       
   115 +
       
   116 +    code = krb5_get_init_creds_keytab(context, kcreds, client_princ,
       
   117 +                                      gsscred->client_keytab, 0, NULL, opt);
       
   118 +    if (code == 0) {
       
   119 +        /* set the gsscred name to that of the princ for which an init cred was
       
   120 +         * acquired. */
       
   121 +        if (gsscred->name != NULL)
       
   122 +            (void) kg_release_name(context, &gsscred->name);
       
   123 +
       
   124 +        code = kg_init_name(context, client_princ, NULL, NULL, NULL,
       
   125 +                            KG_INIT_NAME_NO_COPY, &gsscred->name);
       
   126 +        /* Since KG_INIT_NAME_NO_COPY is set do not free client_princ if
       
   127 +         * kg_init_name succeeds. */
       
   128 +        if (code == 0)
       
   129 +            return 0;
       
   130 +        else
       
   131 +            krb5_free_cred_contents(context, kcreds);
       
   132 +    }
       
   133 +
       
   134 +cleanup:
       
   135 +    krb5_free_principal(context, client_princ);
       
   136 +    return code;
       
   137 +}
       
   138 +
       
   139 +/*
       
   140 + * This implements long time Solaris behavior where processes running as root
       
   141 + * will try to acquire an init cred via the default/system keytab.  The root,
       
   142 + * host and SAM princs are tried in that order until one succeeds or they all
       
   143 + * fail.
       
   144 + */
       
   145 +static krb5_error_code
       
   146 +root_init_cred_kt_fallback(krb5_context context,
       
   147 +                          krb5_creds *kcreds,
       
   148 +                          krb5_gss_cred_id_rec *gsscred,
       
   149 +                          krb5_get_init_creds_opt *opt)
       
   150 +{
       
   151 +    char *sam_name = NULL;
       
   152 +    krb5_error_code code;
       
   153 +
       
   154 +    /* Try the root/<FQDN> service princ in system keytab */
       
   155 +    code = get_root_initcred_keytab(context, kcreds, gsscred, "root",
       
   156 +                                    KRB5_NT_SRV_HST, opt);
       
   157 +    if (code == 0)
       
   158 +        goto out;
       
   159 +
       
   160 +    /* Try the host/<FQDN> service princ in system keytab if the root princ
       
   161 +     * wasn't found */
       
   162 +    code = get_root_initcred_keytab(context, kcreds, gsscred, "host",
       
   163 +                                    KRB5_NT_SRV_HST, opt);
       
   164 +    if (code == 0)
       
   165 +        goto out;
       
   166 +
       
   167 +    /* Try the SAM account princ in system keytab if the host service princ
       
   168 +     * wasn't found for MS interop sake */
       
   169 +    code = get_sam_account_name(&sam_name);
       
   170 +    if (code)
       
   171 +        goto out;
       
   172 +
       
   173 +    code = get_root_initcred_keytab(context, kcreds, gsscred, sam_name,
       
   174 +                                    KRB5_NT_PRINCIPAL, opt);
       
   175 +    free(sam_name);
       
   176 +out:
       
   177 +    return code;
       
   178 +}
       
   179 +
       
   180  /* Get initial credentials using the supplied password or client keytab. */
       
   181  static krb5_error_code
       
   182  get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
       
   183 @@ -609,8 +758,41 @@
       
   184                                              cred->password, NULL, NULL, 0,
       
   185                                              NULL, opt);
       
   186      } else if (cred->client_keytab != NULL) {
       
   187 -        code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
       
   188 -                                          cred->client_keytab, 0, NULL, opt);
       
   189 +        if (krb5_getuid() == 0) {
       
   190 +            char clientktname[BUFSIZ];
       
   191 +
       
   192 +            /* assuming we only need to get the default keytab name once */
       
   193 +            if (defktname[0] == '\0') {
       
   194 +                code = krb5_kt_default_name(context, defktname,
       
   195 +                                            sizeof(defktname));
       
   196 +                if (code)
       
   197 +                    goto cleanup;
       
   198 +            }
       
   199 +
       
   200 +            code = krb5_kt_get_name(context, cred->client_keytab, clientktname,
       
   201 +                                    sizeof(clientktname));
       
   202 +            if (code)
       
   203 +                goto cleanup;
       
   204 +
       
   205 +            /*
       
   206 +             * If the client keytab name is the same as the system default
       
   207 +             * keytab and we are root then we need to use the Solaris root
       
   208 +             * fallback behavior in root_init_cred_kt_fallback().
       
   209 +             */
       
   210 +            if (strcmp(defktname, clientktname) == 0) {
       
   211 +                code = root_init_cred_kt_fallback(context, &creds, cred, opt);
       
   212 +            } else {
       
   213 +                code = krb5_get_init_creds_keytab(context, &creds,
       
   214 +                                                  cred->name->princ,
       
   215 +                                                  cred->client_keytab, 0, NULL,
       
   216 +                                                  opt);
       
   217 +            }
       
   218 +        } else {
       
   219 +            code = krb5_get_init_creds_keytab(context, &creds,
       
   220 +                                              cred->name->princ,
       
   221 +                                              cred->client_keytab, 0, NULL,
       
   222 +                                              opt);
       
   223 +        }
       
   224      } else {
       
   225          code = KRB5_KT_NOTFOUND;
       
   226      }
       
   227 @@ -700,6 +882,23 @@
       
   228              krb5_clear_error_message(context);
       
   229              code = 0;
       
   230          }
       
   231 +        /*
       
   232 +         * The logic below is involved in providing support for Solaris
       
   233 +         * behavior where root processes will fall back to acquiring an initial
       
   234 +         * cred via the system/default keytab.  The idea is that if the
       
   235 +         * client_keytab could not be resolved or it doesn't exist then set the
       
   236 +         * client_keytab field to the system/default keytab.
       
   237 +         */
       
   238 +        if (krb5_getuid() == 0) {
       
   239 +            if (cred->client_keytab == NULL ||
       
   240 +                krb5_kt_have_content(context, cred->client_keytab) != 0) {
       
   241 +
       
   242 +                if (cred->client_keytab != NULL)
       
   243 +                    krb5_kt_close(context, cred->client_keytab);
       
   244 +
       
   245 +                code = krb5_kt_default(context, &cred->client_keytab);
       
   246 +            }
       
   247 +        }
       
   248      }
       
   249      if (code)
       
   250          goto error;
       
   251 --- a/src/lib/krb5/keytab/Makefile.in
       
   252 +++ b/src/lib/krb5/keytab/Makefile.in
       
   253 @@ -13,6 +13,7 @@
       
   254  	ktremove.o	\
       
   255  	ktfns.o		\
       
   256  	kt_file.o	\
       
   257 +	kt_findrealm.o	\
       
   258  	kt_memory.o	\
       
   259  	kt_srvtab.o	\
       
   260  	read_servi.o	\
       
   261 @@ -26,6 +27,7 @@
       
   262  	$(OUTPRE)ktremove.$(OBJEXT)	\
       
   263  	$(OUTPRE)ktfns.$(OBJEXT)	\
       
   264  	$(OUTPRE)kt_file.$(OBJEXT)	\
       
   265 +	$(OUTPRE)kt_findrealm.$(OBJEXT)	\
       
   266  	$(OUTPRE)kt_memory.$(OBJEXT)	\
       
   267  	$(OUTPRE)kt_srvtab.$(OBJEXT)	\
       
   268  	$(OUTPRE)read_servi.$(OBJEXT)	\
       
   269 @@ -39,6 +41,7 @@
       
   270  	$(srcdir)/ktremove.c	\
       
   271  	$(srcdir)/ktfns.c	\
       
   272  	$(srcdir)/kt_file.c	\
       
   273 +	$(srcdir)/kt_findrealm.c	\
       
   274  	$(srcdir)/kt_memory.c	\
       
   275  	$(srcdir)/kt_srvtab.c	\
       
   276  	$(srcdir)/read_servi.c	\