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