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