1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/krb5/krb/gic_keytab.c 9 * 10 * Copyright (C) 2002, 2003 by the Massachusetts Institute of Technology. 11 * All rights reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 */ 32 33 /* Solaris Kerberos */ 34 #include <libintl.h> 35 #include <locale.h> 36 37 #include "k5-int.h" 38 39 /*ARGSUSED*/ 40 static krb5_error_code 41 krb5_get_as_key_keytab( 42 krb5_context context, 43 krb5_principal client, 44 krb5_enctype etype, 45 krb5_prompter_fct prompter, 46 void *prompter_data, 47 krb5_data *salt, 48 krb5_data *params, 49 krb5_keyblock *as_key, 50 void *gak_data) 51 { 52 krb5_keytab keytab = (krb5_keytab) gak_data; 53 krb5_error_code ret; 54 krb5_keytab_entry kt_ent; 55 krb5_keyblock *kt_key; 56 57 /* if there's already a key of the correct etype, we're done. 58 if the etype is wrong, free the existing key, and make 59 a new one. */ 60 61 if (as_key->length) { 62 if (as_key->enctype == etype) 63 return(0); 64 65 krb5_free_keyblock_contents(context, as_key); 66 as_key->length = 0; 67 } 68 69 if (!krb5_c_valid_enctype(etype)) 70 return(KRB5_PROG_ETYPE_NOSUPP); 71 72 /* Solaris Kerberos */ 73 if ((ret = krb5_kt_get_entry(context, keytab, client, 74 0, /* don't have vno available */ 75 etype, &kt_ent)) != NULL) 76 return(ret); 77 78 ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key); 79 80 /* again, krb5's memory management is lame... */ 81 82 *as_key = *kt_key; 83 krb5_xfree(kt_key); 84 85 (void) krb5_kt_free_entry(context, &kt_ent); 86 87 return(ret); 88 } 89 90 krb5_error_code KRB5_CALLCONV 91 krb5_get_init_creds_keytab(krb5_context context, 92 krb5_creds *creds, 93 krb5_principal client, 94 krb5_keytab arg_keytab, 95 krb5_deltat start_time, 96 char *in_tkt_service, 97 krb5_get_init_creds_opt *options) 98 { 99 krb5_error_code ret, ret2; 100 int use_master; 101 krb5_keytab keytab; 102 krb5_gic_opt_ext *opte = NULL; 103 104 if (arg_keytab == NULL) { 105 if ((ret = krb5_kt_default(context, &keytab))) 106 return ret; 107 } else { 108 keytab = arg_keytab; 109 } 110 111 ret = krb5int_gic_opt_to_opte(context, options, &opte, 1, 112 "krb5_get_init_creds_keytab"); 113 if (ret) 114 return ret; 115 116 /* 117 * Solaris Kerberos: 118 * If "client" was constructed from krb5_sname_to_princ() it may 119 * have a referral realm. This happens when there is no applicable 120 * domain-to-realm mapping in the Kerberos configuration file. 121 * If that is the case then the realm of the first principal found 122 * in the keytab which matches the client can be used for the client's 123 * realm. 124 */ 125 if (krb5_is_referral_realm(&client->realm)) { 126 krb5_data realm; 127 ret = krb5_kt_find_realm(context, keytab, client, &realm); 128 if (ret == 0) { 129 krb5_free_data_contents(context, &client->realm); 130 client->realm.length = realm.length; 131 client->realm.data = realm.data; 132 } else { 133 /* Try to set a useful error message */ 134 char *princ = NULL; 135 krb5_unparse_name(context, client, &princ); 136 137 krb5_set_error_message(context, ret, 138 gettext("Failed to find realm for %s in keytab"), 139 princ ? princ : "<unknown>"); 140 if (princ) 141 krb5_free_unparsed_name(context, princ); 142 } 143 } 144 145 if (ret != 0) 146 goto cleanup; 147 148 149 use_master = 0; 150 151 /* first try: get the requested tkt from any kdc */ 152 153 ret = krb5_get_init_creds(context, creds, client, NULL, NULL, 154 start_time, in_tkt_service, opte, 155 krb5_get_as_key_keytab, (void *) keytab, 156 &use_master,NULL); 157 158 /* check for success */ 159 160 if (ret == 0) 161 goto cleanup; 162 163 /* If all the kdc's are unavailable fail */ 164 165 if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE)) 166 goto cleanup; 167 168 /* if the reply did not come from the master kdc, try again with 169 the master kdc */ 170 171 if (!use_master) { 172 use_master = 1; 173 174 ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL, 175 start_time, in_tkt_service, opte, 176 krb5_get_as_key_keytab, (void *) keytab, 177 &use_master, NULL); 178 179 if (ret2 == 0) { 180 ret = 0; 181 goto cleanup; 182 } 183 184 /* if the master is unreachable, return the error from the 185 slave we were able to contact */ 186 187 if ((ret2 == KRB5_KDC_UNREACH) || 188 (ret2 == KRB5_REALM_CANT_RESOLVE) || 189 (ret2 == KRB5_REALM_UNKNOWN)) 190 goto cleanup; 191 192 ret = ret2; 193 } 194 195 /* at this point, we have a response from the master. Since we don't 196 do any prompting or changing for keytabs, that's it. */ 197 198 cleanup: 199 if (opte && krb5_gic_opt_is_shadowed(opte)) 200 krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); 201 if (arg_keytab == NULL) 202 (void) krb5_kt_close(context, keytab); /* Solaris Kerberos */ 203 204 return(ret); 205 } 206 krb5_error_code KRB5_CALLCONV 207 krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options, 208 krb5_address *const *addrs, krb5_enctype *ktypes, 209 krb5_preauthtype *pre_auth_types, 210 krb5_keytab arg_keytab, krb5_ccache ccache, 211 krb5_creds *creds, krb5_kdc_rep **ret_as_reply) 212 { 213 krb5_error_code retval; 214 krb5_gic_opt_ext *opte; 215 char * server = NULL; 216 krb5_keytab keytab; 217 krb5_principal client_princ, server_princ; 218 int use_master = 0; 219 220 retval = krb5int_populate_gic_opt(context, &opte, 221 options, addrs, ktypes, 222 pre_auth_types, creds); 223 if (retval) 224 return retval; 225 226 if (arg_keytab == NULL) { 227 retval = krb5_kt_default(context, &keytab); 228 if (retval) 229 return retval; 230 } 231 else keytab = arg_keytab; 232 233 retval = krb5_unparse_name( context, creds->server, &server); 234 if (retval) 235 goto cleanup; 236 server_princ = creds->server; 237 client_princ = creds->client; 238 retval = krb5_get_init_creds (context, 239 creds, creds->client, 240 krb5_prompter_posix, NULL, 241 0, server, opte, 242 krb5_get_as_key_keytab, (void *)keytab, 243 &use_master, ret_as_reply); 244 krb5_free_unparsed_name( context, server); 245 krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte); 246 if (retval) { 247 goto cleanup; 248 } 249 if (creds->server) 250 krb5_free_principal( context, creds->server); 251 if (creds->client) 252 krb5_free_principal( context, creds->client); 253 creds->client = client_princ; 254 creds->server = server_princ; 255 256 /* store it in the ccache! */ 257 if (ccache) 258 if ((retval = krb5_cc_store_cred(context, ccache, creds))) 259 goto cleanup; 260 cleanup: if (arg_keytab == NULL) 261 krb5_kt_close(context, keytab); 262 return retval; 263 } 264 265