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
get_pw_uid(const char * user,uid_t * uid)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
get_pw_gid(char * user,gid_t * gid)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
get_kmd_kuser(krb5_context kcontext,const char * user,char * kuser,int length)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
key_in_keytab(const char * user,int debug)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