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