xref: /illumos-gate/usr/src/lib/pam_modules/krb5/utils.c (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
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