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