1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #include "k5-int.h" 7 #include "int-proto.h" 8 9 /* Solaris Kerberos */ 10 extern krb5_error_code krb5_libdefault_boolean(); 11 12 static krb5_error_code 13 krb5_cc_copy_creds_except(krb5_context context, krb5_ccache incc, krb5_ccache outcc, krb5_principal princ) 14 { 15 krb5_error_code code; 16 krb5_flags flags; 17 krb5_cc_cursor cur; 18 krb5_creds creds; 19 20 flags = 0; /* turns off OPENCLOSE mode */ 21 /* Solaris Kerberos */ 22 if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL) 23 return(code); 24 /* Solaris Kerberos */ 25 if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL) 26 return(code); 27 28 /* Solaris Kerberos */ 29 if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL) 30 goto cleanup; 31 32 /* Solaris Kerberos */ 33 while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) { 34 if (krb5_principal_compare(context, princ, creds.server)) 35 continue; 36 37 code = krb5_cc_store_cred(context, outcc, &creds); 38 krb5_free_cred_contents(context, &creds); 39 if (code) 40 goto cleanup; 41 } 42 43 if (code != KRB5_CC_END) 44 goto cleanup; 45 46 code = 0; 47 48 cleanup: 49 flags = KRB5_TC_OPENCLOSE; 50 51 /* Solaris Kerberos */ 52 if (code) 53 (void) krb5_cc_set_flags(context, incc, flags); 54 else 55 code = krb5_cc_set_flags(context, incc, flags); 56 57 /* Solaris Kerberos */ 58 if (code) 59 (void) krb5_cc_set_flags(context, outcc, flags); 60 else 61 code = krb5_cc_set_flags(context, outcc, flags); 62 63 return(code); 64 } 65 66 krb5_error_code KRB5_CALLCONV 67 krb5_verify_init_creds(krb5_context context, 68 krb5_creds *creds, 69 krb5_principal server_arg, 70 krb5_keytab keytab_arg, 71 krb5_ccache *ccache_arg, 72 krb5_verify_init_creds_opt *options) 73 { 74 krb5_error_code ret; 75 krb5_principal server; 76 krb5_keytab keytab; 77 krb5_ccache ccache; 78 krb5_keytab_entry kte; 79 krb5_creds in_creds, *out_creds; 80 krb5_auth_context authcon; 81 krb5_data ap_req; 82 83 /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */ 84 85 server = NULL; 86 keytab = NULL; 87 ccache = NULL; 88 out_creds = NULL; 89 authcon = NULL; 90 ap_req.data = NULL; 91 92 /* Solaris Kerberos */ 93 if (server_arg) 94 server = server_arg; 95 else if (ret = krb5_sname_to_principal(context, NULL, NULL, 96 KRB5_NT_SRV_HST, &server)) 97 goto cleanup; 98 99 /* first, check if the server is in the keytab. If not, there's 100 no reason to continue. rd_req does all this, but there's 101 no way to know that a given error is caused by a missing 102 keytab or key, and not by some other problem. */ 103 104 if (keytab_arg) { 105 keytab = keytab_arg; 106 } else { 107 /* Solaris Kerberos: ignore errors here, deal with below */ 108 ret = krb5_kt_default(context, &keytab); 109 } 110 111 /* 112 * Solaris Kerberos: 113 * Warning: be very, very careful when modifying the logic here 114 */ 115 if (keytab == NULL || 116 (ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) { 117 /* this means there is no keying material. This is ok, as long as 118 it is not prohibited by the configuration */ 119 /* Solaris Kerberos */ 120 int nofail = 1; /* Solaris Kerberos: default return error if keytab problems */ 121 122 if (options && 123 (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) { 124 /* first, if options are set then use the option value to set nofail */ 125 nofail = options->ap_req_nofail; 126 } else { 127 /* 128 * Solaris Kerberos: 129 * Check verify_ap_req_nofail if set in config file. Note this logic 130 * assumes that krb5_libdefault_boolean will not set nofail to a 131 * default value if verify_ap_req_nofail is not explictly set in 132 * config file. Don't care about the return code. 133 */ 134 (void) krb5_libdefault_boolean(context, &creds->client->realm, 135 "verify_ap_req_nofail", 136 &nofail); 137 } 138 /* Solaris Kerberos: exit without an error ONLY if nofail is false */ 139 if (!nofail) 140 ret = 0; 141 142 goto cleanup; 143 } 144 145 krb5_kt_free_entry(context, &kte); 146 147 /* If the creds are for the server principal, we're set, just do 148 a mk_req. Otherwise, do a get_credentials first. */ 149 150 if (krb5_principal_compare(context, server, creds->server)) { 151 /* make an ap_req */ 152 if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds, 153 &ap_req))) 154 goto cleanup; 155 } else { 156 /* this is unclean, but it's the easiest way without ripping the 157 library into very small pieces. store the client's initial cred 158 in a memory ccache, then call the library. Later, we'll copy 159 everything except the initial cred into the ccache we return to 160 the user. A clean implementation would involve library 161 internals with a coherent idea of "in" and "out". */ 162 163 /* insert the initial cred into the ccache */ 164 165 if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache))) 166 goto cleanup; 167 /* Solaris Kerberos */ 168 if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL) 169 goto cleanup; 170 171 /* Solaris Kerberos */ 172 if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL) 173 goto cleanup; 174 175 /* set up for get_creds */ 176 memset(&in_creds, 0, sizeof(in_creds)); 177 in_creds.client = creds->client; 178 in_creds.server = server; 179 if ((ret = krb5_timeofday(context, &in_creds.times.endtime))) 180 goto cleanup; 181 in_creds.times.endtime += 5*60; 182 183 if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds, 184 &out_creds))) 185 goto cleanup; 186 187 /* make an ap_req */ 188 if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, 189 &ap_req))) 190 goto cleanup; 191 } 192 193 /* wipe the auth context for mk_req */ 194 if (authcon) { 195 krb5_auth_con_free(context, authcon); 196 authcon = NULL; 197 } 198 199 /* verify the ap_req */ 200 201 if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, 202 NULL, NULL))) 203 goto cleanup; 204 205 /* if we get this far, then the verification succeeded. We can 206 still fail if the library stuff here fails, but that's it */ 207 208 if (ccache_arg && ccache) { 209 if (*ccache_arg == NULL) { 210 krb5_ccache retcc; 211 212 retcc = NULL; 213 214 /* Solaris Kerberos */ 215 if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) || 216 ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) || 217 ((ret = krb5_cc_copy_creds_except(context, ccache, retcc, 218 creds->server)) != NULL)) { 219 /* Solaris Kerberos */ 220 if (retcc) 221 (void) krb5_cc_destroy(context, retcc); 222 } else { 223 *ccache_arg = retcc; 224 } 225 } else { 226 ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg, 227 server); 228 } 229 } 230 231 /* if any of the above paths returned an errors, then ret is set 232 accordingly. either that, or it's zero, which is fine, too */ 233 234 cleanup: 235 if (!server_arg && server) 236 krb5_free_principal(context, server); 237 /* Solaris Kerberos */ 238 if (!keytab_arg && keytab) 239 (void) krb5_kt_close(context, keytab); 240 /* Solaris Kerberos */ 241 if (ccache) 242 (void) krb5_cc_destroy(context, ccache); 243 if (out_creds) 244 krb5_free_creds(context, out_creds); 245 if (authcon) 246 krb5_auth_con_free(context, authcon); 247 if (ap_req.data) 248 krb5_xfree(ap_req.data); 249 250 return(ret); 251 } 252