1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/krb5/ccache/ccselect_hostname.c - hostname ccselect module */ 3 /* 4 * Copyright (C) 2017 by Red Hat, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "k5-int.h" 34 #include "cc-int.h" 35 #include <ctype.h> 36 #include <krb5/ccselect_plugin.h> 37 38 /* Swap a and b, using tmp as an intermediate. */ 39 #define SWAP(a, b, tmp) \ 40 tmp = a; \ 41 a = b; \ 42 b = tmp; 43 44 static krb5_error_code 45 hostname_init(krb5_context context, krb5_ccselect_moddata *data_out, 46 int *priority_out) 47 { 48 *data_out = NULL; 49 *priority_out = KRB5_CCSELECT_PRIORITY_HEURISTIC; 50 return 0; 51 } 52 53 static krb5_error_code 54 hostname_choose(krb5_context context, krb5_ccselect_moddata data, 55 krb5_principal server, krb5_ccache *ccache_out, 56 krb5_principal *princ_out) 57 { 58 krb5_error_code ret; 59 char *p, *host = NULL; 60 size_t hostlen; 61 krb5_cccol_cursor col_cursor; 62 krb5_ccache ccache, tmp_ccache, best_ccache = NULL; 63 krb5_principal princ, tmp_princ, best_princ = NULL; 64 krb5_data domain; 65 66 *ccache_out = NULL; 67 *princ_out = NULL; 68 69 if (server->type != KRB5_NT_SRV_HST || server->length < 2) 70 return KRB5_PLUGIN_NO_HANDLE; 71 72 /* Compute upper-case hostname. */ 73 hostlen = server->data[1].length; 74 host = k5memdup0(server->data[1].data, hostlen, &ret); 75 if (host == NULL) 76 return ret; 77 for (p = host; *p != '\0'; p++) { 78 if (islower(*p)) 79 *p = toupper(*p); 80 } 81 82 /* Scan the collection for a cache with a client principal whose realm is 83 * the longest tail of the server hostname. */ 84 ret = krb5_cccol_cursor_new(context, &col_cursor); 85 if (ret) 86 goto done; 87 88 for (ret = krb5_cccol_cursor_next(context, col_cursor, &ccache); 89 ret == 0 && ccache != NULL; 90 ret = krb5_cccol_cursor_next(context, col_cursor, &ccache)) { 91 ret = krb5_cc_get_principal(context, ccache, &princ); 92 if (ret) { 93 krb5_cc_close(context, ccache); 94 break; 95 } 96 97 /* Check for a longer match than we have. */ 98 domain = make_data(host, hostlen); 99 while (best_princ == NULL || 100 best_princ->realm.length < domain.length) { 101 if (data_eq(princ->realm, domain)) { 102 SWAP(best_ccache, ccache, tmp_ccache); 103 SWAP(best_princ, princ, tmp_princ); 104 break; 105 } 106 107 /* Try the next parent domain. */ 108 p = memchr(domain.data, '.', domain.length); 109 if (p == NULL) 110 break; 111 domain = make_data(p + 1, hostlen - (p + 1 - host)); 112 } 113 114 if (ccache != NULL) 115 krb5_cc_close(context, ccache); 116 krb5_free_principal(context, princ); 117 } 118 119 krb5_cccol_cursor_free(context, &col_cursor); 120 121 if (best_ccache != NULL) { 122 *ccache_out = best_ccache; 123 *princ_out = best_princ; 124 } else { 125 ret = KRB5_PLUGIN_NO_HANDLE; 126 } 127 128 done: 129 free(host); 130 return ret; 131 } 132 133 krb5_error_code 134 ccselect_hostname_initvt(krb5_context context, int maj_ver, int min_ver, 135 krb5_plugin_vtable vtable) 136 { 137 krb5_ccselect_vtable vt; 138 139 if (maj_ver != 1) 140 return KRB5_PLUGIN_VER_NOTSUPP; 141 vt = (krb5_ccselect_vtable)vtable; 142 vt->name = "hostname"; 143 vt->init = hostname_init; 144 vt->choose = hostname_choose; 145 return 0; 146 } 147