1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <security/pam_appl.h> 27 #include <pwd.h> 28 #include <string.h> 29 #include <stdlib.h> 30 #include <malloc.h> 31 #include <unistd.h> 32 #include <ctype.h> 33 #include <syslog.h> 34 #include <errno.h> 35 36 #include "utils.h" 37 38 extern const char *error_message(long); 39 40 /* ******************************************************************** */ 41 /* */ 42 /* Utilities Functions */ 43 /* */ 44 /* ******************************************************************** */ 45 46 /* 47 * get_pw_uid(): 48 * To get the uid from the passwd entry for specified user 49 * It returns 0 if the user can't be found, otherwise returns 1. 50 */ 51 int 52 get_pw_uid(char *user, uid_t *uid) 53 { 54 struct passwd sp; 55 char buffer[1024]; 56 57 if (getpwnam_r(user, &sp, buffer, sizeof (buffer)) == NULL) { 58 return (0); 59 } 60 61 *uid = sp.pw_uid; 62 63 return (1); 64 } 65 66 /* 67 * get_pw_gid(): 68 * To get the gid from the passwd entry for specified user 69 * It returns 0 if the user can't be found, otherwise returns 1. 70 */ 71 int 72 get_pw_gid(char *user, gid_t *gid) 73 { 74 struct passwd sp; 75 char buffer[1024]; 76 77 if (getpwnam_r(user, &sp, buffer, sizeof (buffer)) == NULL) { 78 return (0); 79 } 80 81 *gid = sp.pw_gid; 82 83 return (1); 84 } 85 86 87 /* 88 * get_kmd_kuser(): 89 * To get the kerberos user name for the specified user. 90 * Assumes that the kuser string is allocated. It will be 91 * overwritten. This saves us having to deal will allocating 92 * and freeing the kuser string. 93 * 94 * RFC 1510 does not mention how to handle mixed case domainnames 95 * while constructing client principals. So we will follow the same 96 * procedure as for server principals and lowercase the domainname. 97 * 98 * Returns: 99 * PAM_BUF_ERR - if there is an error from krb5_sname_to_principal(), 100 * or krb5_unparse_name() 101 * 0 - if there was no error 102 */ 103 int 104 get_kmd_kuser(krb5_context kcontext, const char *user, char *kuser, int length) 105 { 106 if (strcmp(user, ROOT_UNAME) == 0) { 107 krb5_principal princ; 108 char *name, *princname, *lasts; 109 110 if (krb5_sname_to_principal(kcontext, NULL, ROOT_UNAME, 111 KRB5_NT_SRV_HST, &princ)) { 112 return (PAM_BUF_ERR); 113 } 114 if (krb5_unparse_name(kcontext, princ, &princname)) { 115 krb5_free_principal(kcontext, princ); 116 return (PAM_BUF_ERR); 117 } 118 /* just interested in princ name before the @REALM part */ 119 if ((name = strtok_r(princname, "@", &lasts)) == NULL) { 120 krb5_free_principal(kcontext, princ); 121 free(princname); 122 return (PAM_BUF_ERR); 123 } 124 if (strlcpy(kuser, name, length) >= length) { 125 krb5_free_principal(kcontext, princ); 126 free(princname); 127 return (PAM_BUF_ERR); 128 } 129 krb5_free_principal(kcontext, princ); 130 free(princname); 131 } else { 132 if (strlcpy(kuser, user, length) >= length) { 133 return (PAM_BUF_ERR); 134 } 135 } 136 return (0); 137 } 138 139 /* 140 * return true (1) if the user's key is in the (default) keytab 141 */ 142 int 143 key_in_keytab(const char *user, int debug) 144 { 145 krb5_keytab kt_handle; 146 krb5_keytab_entry kt_ent; 147 char *whoami = "key_in_keytab"; 148 krb5_error_code retval = 0; 149 krb5_error_code code = 0; 150 krb5_context kcontext = NULL; 151 krb5_principal princ = NULL; 152 char kuser[2*MAXHOSTNAMELEN]; 153 154 155 if (debug) 156 __pam_log(LOG_AUTH | LOG_DEBUG, 157 "PAM-KRB5 (%s): start for user '%s'", 158 whoami, user ? user : "<null>"); 159 160 if (!user) 161 return (retval); 162 163 /* need to free context with krb5_free_context */ 164 if (code = krb5_init_secure_context(&kcontext)) { 165 if (debug) 166 __pam_log(LOG_AUTH | LOG_DEBUG, 167 "PAM-KRB5 (%s): Error initializing " 168 "krb5: %s", whoami, 169 error_message(code)); 170 return (retval); 171 } 172 173 if ((code = get_kmd_kuser(kcontext, (const char *)user, kuser, 174 2*MAXHOSTNAMELEN)) != 0) { 175 goto out; 176 } 177 178 /* need to free princ with krb5_free_principal */ 179 if ((code = krb5_parse_name(kcontext, kuser, &princ)) != 0) { 180 if (debug) 181 __pam_log(LOG_AUTH | LOG_DEBUG, 182 "PAM-KRB5 (%s): can't parse name (%s)", 183 whoami, error_message(code)); 184 goto out; 185 } 186 187 /* need to close keytab handle with krb5_kt_close */ 188 if ((code = krb5_kt_default(kcontext, &kt_handle))) { 189 if (debug) 190 __pam_log(LOG_AUTH | LOG_DEBUG, 191 "PAM-KRB5 (%s): krb5_kt_default failed (%s)", 192 whoami, error_message(code)); 193 goto out; 194 } 195 196 code = krb5_kt_get_entry(kcontext, kt_handle, princ, 0, 0, &kt_ent); 197 if (code != 0) { 198 if (code == ENOENT) { 199 if (debug) 200 __pam_log(LOG_AUTH | LOG_DEBUG, 201 "PAM-KRB5 (%s): " 202 "Keytab does not exist", 203 whoami); 204 } else if (code == KRB5_KT_NOTFOUND) { 205 if (debug) 206 __pam_log(LOG_AUTH | LOG_DEBUG, 207 "PAM-KRB5 (%s): " 208 "No entry for principal " 209 "'%s' exists in keytab", 210 whoami, kuser); 211 } else { 212 if (debug) 213 __pam_log(LOG_AUTH | LOG_DEBUG, 214 "PAM-KRB5 (%s): " 215 "krb5_kt_get_entry failed (%s)", 216 whoami, error_message(code)); 217 } 218 } else { /* Key found in keytab, return success */ 219 (void) krb5_kt_free_entry(kcontext, &kt_ent); 220 if (debug) 221 __pam_log(LOG_AUTH | LOG_DEBUG, 222 "PAM-KRB5 (%s): " 223 "keytab entry for '%s' found", 224 whoami, user); 225 retval = 1; 226 } 227 228 (void) krb5_kt_close(kcontext, kt_handle); 229 out: 230 if (princ && kcontext) 231 krb5_free_principal(kcontext, princ); 232 233 if (kcontext) 234 krb5_free_context(kcontext); 235 236 return (retval); 237 } 238