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