1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/krb5/ccache/ccselect_k5identity.c - k5identity ccselect module */ 3 /* 4 * Copyright (C) 2011 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include "k5-int.h" 28 #include "cc-int.h" 29 #include <krb5/ccselect_plugin.h> 30 #include <ctype.h> 31 32 #ifndef _WIN32 33 34 #include <pwd.h> 35 36 static krb5_error_code 37 k5identity_init(krb5_context context, krb5_ccselect_moddata *data_out, 38 int *priority_out) 39 { 40 *data_out = NULL; 41 *priority_out = KRB5_CCSELECT_PRIORITY_AUTHORITATIVE; 42 return 0; 43 } 44 45 /* Match data (folded to lowercase if fold_case is set) against pattern. */ 46 static krb5_boolean 47 fnmatch_data(const char *pattern, krb5_data *data, krb5_boolean fold_case) 48 { 49 krb5_error_code ret; 50 char *str, *p; 51 int res; 52 53 str = k5memdup0(data->data, data->length, &ret); 54 if (str == NULL) 55 return FALSE; 56 57 if (fold_case) { 58 for (p = str; *p != '\0'; p++) { 59 if (isupper((unsigned char)*p)) 60 *p = tolower((unsigned char)*p); 61 } 62 } 63 64 res = fnmatch(pattern, str, 0); 65 free(str); 66 return (res == 0); 67 } 68 69 /* Return true if server satisfies the constraint given by name and value. */ 70 static krb5_boolean 71 check_constraint(krb5_context context, const char *name, const char *value, 72 krb5_principal server) 73 { 74 if (strcmp(name, "realm") == 0) { 75 return fnmatch_data(value, &server->realm, FALSE); 76 } else if (strcmp(name, "service") == 0) { 77 return (server->type == KRB5_NT_SRV_HST && server->length >= 2 && 78 fnmatch_data(value, &server->data[0], FALSE)); 79 } else if (strcmp(name, "host") == 0) { 80 return (server->type == KRB5_NT_SRV_HST && server->length >= 2 && 81 fnmatch_data(value, &server->data[1], TRUE)); 82 } 83 /* Assume unrecognized constraints are critical. */ 84 return FALSE; 85 } 86 87 /* 88 * If line begins with a valid principal and server matches the constraints 89 * listed afterwards, set *princ_out to the client principal described in line 90 * and return true. Otherwise return false. May destructively affect line. 91 */ 92 static krb5_boolean 93 parse_line(krb5_context context, char *line, krb5_principal server, 94 krb5_principal *princ_out) 95 { 96 const char *whitespace = " \t\r\n"; 97 char *princ, *princ_end, *field, *field_end, *sep; 98 99 *princ_out = NULL; 100 101 /* Find the bounds of the principal. */ 102 princ = line + strspn(line, whitespace); 103 if (*princ == '#') 104 return FALSE; 105 princ_end = princ + strcspn(princ, whitespace); 106 if (princ_end == princ) 107 return FALSE; 108 109 /* Check all constraints. */ 110 field = princ_end + strspn(princ_end, whitespace); 111 while (*field != '\0') { 112 field_end = field + strcspn(field, whitespace); 113 if (*field_end != '\0') 114 *field_end++ = '\0'; 115 sep = strchr(field, '='); 116 if (sep == NULL) /* Malformed line. */ 117 return FALSE; 118 *sep = '\0'; 119 if (!check_constraint(context, field, sep + 1, server)) 120 return FALSE; 121 field = field_end + strspn(field_end, whitespace); 122 } 123 124 *princ_end = '\0'; 125 return (krb5_parse_name(context, princ, princ_out) == 0); 126 } 127 128 /* Determine the current user's homedir. Allow HOME to override the result for 129 * non-secure profiles; otherwise, use the euid's homedir from passwd. */ 130 static char * 131 get_homedir(krb5_context context) 132 { 133 const char *homedir = NULL; 134 char pwbuf[BUFSIZ]; 135 struct passwd pwx, *pwd; 136 137 if (!context->profile_secure) 138 homedir = secure_getenv("HOME"); 139 140 if (homedir == NULL) { 141 if (k5_getpwuid_r(geteuid(), &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0) 142 return NULL; 143 homedir = pwd->pw_dir; 144 } 145 146 return strdup(homedir); 147 } 148 149 static krb5_error_code 150 k5identity_choose(krb5_context context, krb5_ccselect_moddata data, 151 krb5_principal server, krb5_ccache *cache_out, 152 krb5_principal *princ_out) 153 { 154 krb5_error_code ret; 155 krb5_principal princ = NULL; 156 char *filename, *homedir; 157 FILE *fp; 158 char buf[256]; 159 160 *cache_out = NULL; 161 *princ_out = NULL; 162 163 /* Open the .k5identity file. */ 164 homedir = get_homedir(context); 165 if (homedir == NULL) 166 return KRB5_PLUGIN_NO_HANDLE; 167 ret = k5_path_join(homedir, ".k5identity", &filename); 168 free(homedir); 169 if (ret) 170 return ret; 171 fp = fopen(filename, "r"); 172 free(filename); 173 if (fp == NULL) 174 return KRB5_PLUGIN_NO_HANDLE; 175 176 /* Look for a line with constraints matched by server. */ 177 while (fgets(buf, sizeof(buf), fp) != NULL) { 178 if (parse_line(context, buf, server, &princ)) 179 break; 180 } 181 fclose(fp); 182 if (princ == NULL) 183 return KRB5_PLUGIN_NO_HANDLE; 184 185 /* Look for a ccache with the appropriate client principal. If we don't 186 * find out, set *princ_out to indicate the desired client principal. */ 187 ret = krb5_cc_cache_match(context, princ, cache_out); 188 if (ret == 0 || ret == KRB5_CC_NOTFOUND) 189 *princ_out = princ; 190 else 191 krb5_free_principal(context, princ); 192 return ret; 193 } 194 195 krb5_error_code 196 ccselect_k5identity_initvt(krb5_context context, int maj_ver, int min_ver, 197 krb5_plugin_vtable vtable) 198 { 199 krb5_ccselect_vtable vt; 200 201 if (maj_ver != 1) 202 return KRB5_PLUGIN_VER_NOTSUPP; 203 vt = (krb5_ccselect_vtable)vtable; 204 vt->name = "k5identity"; 205 vt->init = k5identity_init; 206 vt->choose = k5identity_choose; 207 return 0; 208 } 209 210 #endif /* not _WIN32 */ 211