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