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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <security/pam_appl.h> 30 #include <pwd.h> 31 #include <string.h> 32 #include <stdlib.h> 33 #include <malloc.h> 34 #include <unistd.h> 35 #include <ctype.h> 36 #include <syslog.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_AUTH_ERR - if local host name is not found 102 * PAM_BUF_ERR - if there is an error from krb5_sname_to_principal(), 103 * or krb5_unparse_name() 104 * 0 - if there was no error 105 */ 106 int 107 get_kmd_kuser(krb5_context kcontext, const char *user, char *kuser, int length) 108 { 109 if (strcmp(user, ROOT_UNAME) == 0) { 110 krb5_principal princ; 111 char *name, *princname, *lasts; 112 113 if (krb5_sname_to_principal(kcontext, NULL, ROOT_UNAME, 114 KRB5_NT_SRV_HST, &princ)) { 115 return (PAM_BUF_ERR); 116 } 117 if (krb5_unparse_name(kcontext, princ, &princname)) { 118 krb5_free_principal(kcontext, princ); 119 return (PAM_BUF_ERR); 120 } 121 /* just interested in princ name before the @REALM part */ 122 if ((name = strtok_r(princname, "@", &lasts)) == NULL) { 123 krb5_free_principal(kcontext, princ); 124 free(princname); 125 return (PAM_BUF_ERR); 126 } 127 if (strlcpy(kuser, name, length) >= length) { 128 krb5_free_principal(kcontext, princ); 129 free(princname); 130 return (PAM_BUF_ERR); 131 } 132 krb5_free_principal(kcontext, princ); 133 free(princname); 134 } else { 135 if (strlcpy(kuser, user, length) >= length) { 136 return (PAM_BUF_ERR); 137 } 138 } 139 return (0); 140 } 141 142 /* 143 * return true (1) if the user's key is in the (default) keytab 144 */ 145 int 146 key_in_keytab(const char *user, int debug) 147 { 148 krb5_keytab kt_handle; 149 krb5_keytab_entry kt_ent; 150 char *whoami = "key_in_keytab"; 151 krb5_error_code retval = 0; 152 krb5_error_code code = 0; 153 krb5_context kcontext = NULL; 154 krb5_principal princ = NULL; 155 char kuser[2*MAXHOSTNAMELEN]; 156 157 158 if (debug) 159 syslog(LOG_DEBUG, 160 "PAM-KRB5 (%s): start for user '%s'", 161 whoami, user ? user : "<null>"); 162 163 if (!user) 164 return (retval); 165 166 /* need to free context with krb5_free_context */ 167 if (code = krb5_init_context(&kcontext)) { 168 if (debug) 169 syslog(LOG_DEBUG, 170 "PAM-KRB5 (%s): Error initializing " 171 "krb5: %s", whoami, 172 error_message(code)); 173 return (retval); 174 } 175 176 if ((code = get_kmd_kuser(kcontext, (const char *)user, kuser, 177 2*MAXHOSTNAMELEN)) != 0) { 178 goto out; 179 } 180 181 /* need to free princ with krb5_free_principal */ 182 if ((code = krb5_parse_name(kcontext, kuser, &princ)) != 0) { 183 if (debug) 184 syslog(LOG_DEBUG, 185 "PAM-KRB5 (%s): can't parse name (%s)", 186 whoami, error_message(code)); 187 goto out; 188 } 189 190 /* need to close keytab handle with krb5_kt_close */ 191 if ((code = krb5_kt_default(kcontext, &kt_handle))) { 192 if (debug) 193 syslog(LOG_DEBUG, 194 "PAM-KRB5 (%s): krb5_kt_default failed (%s)", 195 whoami, error_message(code)); 196 goto out; 197 } 198 199 code = krb5_kt_get_entry(kcontext, kt_handle, princ, 0, 0, &kt_ent); 200 if (code != 0) { 201 if (code == ENOENT) { 202 if (debug) 203 syslog(LOG_DEBUG, 204 "PAM-KRB5 (%s): " 205 "Keytab does not exist", 206 whoami); 207 } else if (code == KRB5_KT_NOTFOUND) { 208 if (debug) 209 syslog(LOG_DEBUG, 210 "PAM-KRB5 (%s): " 211 "No entry for principal " 212 "'%s' exists in keytab", 213 whoami, kuser); 214 } else { 215 if (debug) 216 syslog(LOG_DEBUG, 217 "PAM-KRB5 (%s): " 218 "krb5_kt_get_entry failed (%s)", 219 whoami, error_message(code)); 220 } 221 } else { /* Key found in keytab, return success */ 222 (void) krb5_kt_free_entry(kcontext, &kt_ent); 223 if (debug) 224 syslog(LOG_DEBUG, 225 "PAM-KRB5 (%s): " 226 "keytab entry for '%s' found", 227 whoami, user); 228 retval = 1; 229 } 230 231 (void) krb5_kt_close(kcontext, kt_handle); 232 out: 233 if (princ && kcontext) 234 krb5_free_principal(kcontext, princ); 235 236 if (kcontext) 237 krb5_free_context(kcontext); 238 239 return (retval); 240 } 241