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 9 extern krb5_error_code krb5_libdefault_boolean(); 10 11 static krb5_error_code 12 krb5_cc_copy_creds_except(context, incc, outcc, princ) 13 krb5_context context; 14 krb5_ccache incc; 15 krb5_ccache outcc; 16 krb5_principal princ; 17 { 18 krb5_error_code code; 19 krb5_flags flags; 20 krb5_cc_cursor cur; 21 krb5_creds creds; 22 23 flags = 0; /* turns off OPENCLOSE mode */ 24 if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL) 25 return(code); 26 if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL) 27 return(code); 28 29 if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL) 30 goto cleanup; 31 32 while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) { 33 if (krb5_principal_compare(context, princ, creds.server)) 34 continue; 35 36 code = krb5_cc_store_cred(context, outcc, &creds); 37 krb5_free_cred_contents(context, &creds); 38 if (code) 39 goto cleanup; 40 } 41 42 if (code != KRB5_CC_END) 43 goto cleanup; 44 45 code = 0; 46 47 cleanup: 48 flags = KRB5_TC_OPENCLOSE; 49 50 if (code) 51 (void) krb5_cc_set_flags(context, incc, flags); 52 else 53 code = krb5_cc_set_flags(context, incc, flags); 54 55 if (code) 56 (void) krb5_cc_set_flags(context, outcc, flags); 57 else 58 code = krb5_cc_set_flags(context, outcc, flags); 59 60 return(code); 61 } 62 63 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV 64 krb5_verify_init_creds(krb5_context context, 65 krb5_creds *creds, 66 krb5_principal server_arg, 67 krb5_keytab keytab_arg, 68 krb5_ccache *ccache_arg, 69 krb5_verify_init_creds_opt *options) 70 { 71 krb5_error_code ret; 72 krb5_principal server; 73 krb5_keytab keytab; 74 krb5_ccache ccache; 75 krb5_keytab_entry kte; 76 krb5_creds in_creds, *out_creds; 77 krb5_auth_context authcon; 78 krb5_data ap_req; 79 80 /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */ 81 82 server = NULL; 83 keytab = NULL; 84 ccache = NULL; 85 out_creds = NULL; 86 authcon = NULL; 87 ap_req.data = NULL; 88 89 if (server_arg) { 90 server = server_arg; 91 } else { 92 if (ret = krb5_sname_to_principal(context, NULL, NULL, 93 KRB5_NT_SRV_HST, &server)) { 94 goto cleanup; 95 } else { 96 /* 97 * Solaris Kerberos: 98 * We check first up to see whether 'verify_ap_req_fail' is 99 * set to false, because if FALSE there is no point in 100 * proceeding any further with the strict TGT verification check 101 * for the 'host/fqdn' service principal in the local keytab. 102 */ 103 int nofail; 104 if (krb5_libdefault_boolean(context, 105 &creds->client->realm, 106 "verify_ap_req_nofail", 107 &nofail) == 0) { 108 /* 109 * Solaris Kerberos: 110 * If the administrator has configured the system such 111 * that its OK to fail this strict TGT verification check 112 * (i.e. verify_ap_req_nofail = false), set the 113 * 'ret' code to 0 and cleanup. 114 */ 115 if (!nofail) { 116 ret = 0; 117 goto cleanup; 118 } 119 } 120 } 121 } 122 123 /* first, check if the server is in the keytab. If not, there's 124 no reason to continue. rd_req does all this, but there's 125 no way to know that a given error is caused by a missing 126 keytab or key, and not by some other problem. */ 127 128 if (keytab_arg) { 129 keytab = keytab_arg; 130 } else { 131 if (ret = krb5_kt_default(context, &keytab)) 132 goto cleanup; 133 } 134 135 if ((ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte)) != NULL) { 136 /* this means there is no keying material. This is ok, as long as 137 it is not prohibited by the configuration */ 138 if (options && 139 (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) { 140 if (options->ap_req_nofail) 141 goto cleanup; 142 } 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 168 if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL) 169 goto cleanup; 170 171 if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL) 172 goto cleanup; 173 174 /* set up for get_creds */ 175 memset(&in_creds, 0, sizeof(in_creds)); 176 in_creds.client = creds->client; 177 in_creds.server = server; 178 if (ret = krb5_timeofday(context, &in_creds.times.endtime)) 179 goto cleanup; 180 in_creds.times.endtime += 5*60; 181 182 if (ret = krb5_get_credentials(context, 0, ccache, &in_creds, 183 &out_creds)) 184 goto cleanup; 185 186 /* make an ap_req */ 187 if (ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, 188 &ap_req)) 189 goto cleanup; 190 } 191 192 /* wipe the auth context for mk_req */ 193 if (authcon) { 194 krb5_auth_con_free(context, authcon); 195 authcon = NULL; 196 } 197 198 /* verify the ap_req */ 199 200 if (ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, 201 NULL, NULL)) 202 goto cleanup; 203 204 /* if we get this far, then the verification succeeded. We can 205 still fail if the library stuff here fails, but that's it */ 206 207 if (ccache_arg && ccache) { 208 if (*ccache_arg == NULL) { 209 krb5_ccache retcc; 210 211 retcc = NULL; 212 213 if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) || 214 ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) || 215 ((ret = krb5_cc_copy_creds_except(context, ccache, retcc, 216 creds->server)) != NULL)) { 217 if (retcc) 218 (void) krb5_cc_destroy(context, retcc); 219 } else { 220 *ccache_arg = retcc; 221 } 222 } else { 223 ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg, 224 server); 225 } 226 } 227 228 /* if any of the above paths returned an errors, then ret is set 229 accordingly. either that, or it's zero, which is fine, too */ 230 231 cleanup: 232 if (!server_arg && server) 233 krb5_free_principal(context, server); 234 if (!keytab_arg && keytab) 235 (void) krb5_kt_close(context, keytab); 236 if (ccache) 237 (void) krb5_cc_destroy(context, ccache); 238 if (out_creds) 239 krb5_free_creds(context, out_creds); 240 if (authcon) 241 krb5_auth_con_free(context, authcon); 242 if (ap_req.data) 243 krb5_xfree(ap_req.data); 244 245 return(ret); 246 } 247