1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <pwd.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <syslog.h>
9 #include <grp.h>
10
11 #include "smbftpd.h"
12
13 #ifdef USE_PAM
14
15 extern smbftpd_session_t smbftpd_session;
16
17 #include <security/pam_appl.h>
18
19 pam_handle_t *pamh = NULL;
20
21 struct usercache {
22 char *user;
23 char *home;
24 uid_t uid;
25 gid_t gid;
26 };
27 static struct usercache pwcache;
28
user_cache_free(struct usercache * p)29 static void user_cache_free(struct usercache *p)
30 {
31 if (p->user) free(p->user);
32 if (p->home) free(p->home);
33 p->user = NULL;
34 p->home = NULL;
35 p->uid = -1;
36 p->gid = -1;
37 }
38
39 /*
40 * the following code is stolen from imap-uw PAM authentication module and
41 * login.c
42 */
43 #define COPY_STRING(s) (s ? strdup(s) : NULL)
44
45 struct cred_t {
46 const char *uname; /* user name */
47 const char *pass; /* password */
48 };
49 typedef struct cred_t cred_t;
50
auth_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata)51 static int auth_conv(int num_msg, const struct pam_message **msg,
52 struct pam_response **resp, void *appdata)
53 {
54 int i;
55 cred_t *cred = (cred_t *) appdata;
56 struct pam_response *response;
57
58 response = calloc(num_msg, sizeof *response);
59 if (response == NULL)
60 return PAM_BUF_ERR;
61
62 for (i = 0; i < num_msg; i++) {
63 switch (msg[i]->msg_style) {
64 case PAM_PROMPT_ECHO_ON: /* assume want user name */
65 response[i].resp_retcode = PAM_SUCCESS;
66 response[i].resp = COPY_STRING(cred->uname);
67 /* PAM frees resp. */
68 break;
69 case PAM_PROMPT_ECHO_OFF: /* assume want password */
70 response[i].resp_retcode = PAM_SUCCESS;
71 response[i].resp = COPY_STRING(cred->pass);
72 /* PAM frees resp. */
73 break;
74 case PAM_TEXT_INFO:
75 case PAM_ERROR_MSG:
76 response[i].resp_retcode = PAM_SUCCESS;
77 response[i].resp = NULL;
78 break;
79 default: /* unknown message style */
80 free(response);
81 return PAM_CONV_ERR;
82 }
83 }
84
85 *resp = response;
86 return PAM_SUCCESS;
87 }
88
89 /**
90 * There is no config parser for PAM authentication. This function just
91 * initial the user cache.
92 *
93 * @param path
94 *
95 * @return Always return 0.
96 */
auth_pam_config_parse(const char * path)97 int auth_pam_config_parse(const char *path)
98 {
99 /* Initial user cache when daemon starts */
100 user_cache_free(&pwcache);
101
102 return 0;
103 }
104
105 /**
106 * Attempt to authenticate the user using PAM. Returns 0 if the user is
107 * authenticated, or -1 if not authenticated.
108 *
109 * If some sort of PAM system error occurs (e.g., the "/etc/pam.d/ftpd"
110 * is missing) then this function returns -1, too.
111 *
112 * @param user
113 * @param password
114 *
115 * @return 0: User is authenticated
116 * -1: Not authenticated or PAM system error
117 */
auth_pam_check(const char * user,const char * password)118 int auth_pam_check(const char *user, const char *password)
119 {
120 cred_t auth_cred = { user, password};
121 struct pam_conv conv = { &auth_conv, &auth_cred};
122 struct passwd *pw;
123 int rval = -1;
124 int e;
125
126 /* Initial user cache before login. User can login with different name
127 * in the same session. */
128 user_cache_free(&pwcache);
129
130 e = pam_start("ftpd", user, &conv, &pamh);
131 if (e != PAM_SUCCESS) {
132 syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
133 return -1;
134 }
135
136 e = pam_set_item(pamh, PAM_RHOST, smbftpd_session.remotehost);
137 if (e != PAM_SUCCESS) {
138 syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
139 pam_strerror(pamh, e));
140 return -1;
141 }
142
143 e = pam_authenticate(pamh, 0);
144 switch (e) {
145 case PAM_SUCCESS:
146 // User/Password is valid
147 break;
148
149 case PAM_AUTH_ERR:
150 case PAM_USER_UNKNOWN:
151 case PAM_MAXTRIES:
152 // Authentication failed
153 goto Error;
154 default:
155 syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
156 goto Error;
157 }
158
159 pw = getpwnam(user);
160 if (!pw) {
161 goto Error;
162 }
163
164 e = pam_acct_mgmt(pamh, 0);
165 if (e != PAM_SUCCESS) {
166 syslog(LOG_ERR, "pam_acct_mgmt: %s", pam_strerror(pamh, e));
167 goto Error;
168 }
169
170 e = pam_setcred(pamh, PAM_ESTABLISH_CRED);
171 if (e != PAM_SUCCESS) {
172 syslog(LOG_ERR, "%s (%d)pam_setcred: %s", __FILE__, __LINE__, pam_strerror(pamh, e));
173 goto Error;
174 }
175
176 pam_open_session(pamh, 0);
177 pam_close_session(pamh,0);
178
179 pwcache.user = strdup(user);
180 pwcache.home = strdup(pw->pw_dir);
181 pwcache.uid = pw->pw_uid;
182 pwcache.gid = pw->pw_gid;
183
184 rval = 0;
185 Error:
186 if (pamh) {
187 if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
188 syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
189 }
190 pamh = NULL;
191 }
192 return rval;
193 }
194
195 /**
196 * Close/free config and user cache.
197 */
auth_pam_config_free()198 void auth_pam_config_free()
199 {
200 user_cache_free(&pwcache);
201
202 return;
203 }
204
205 /**
206 * Check whether user belongs to the group.
207 *
208 * We will:
209 * 1. getpwnam() to check whether pw_gid is the same with the group's
210 * gr_gid from getgrnam.
211 * 2. Check whether user is in group's gr_mem.
212 *
213 * @param user The user name to check
214 * @param group The group name to check
215 *
216 * @return 1: Yes, user belongs to the group
217 * 0: No, user does not belong to the group
218 */
auth_pam_is_user_in_group(const char * user,const char * group)219 int auth_pam_is_user_in_group(const char *user, const char *group)
220 {
221 struct group *grp = NULL;
222 gid_t gid;
223 struct passwd *pw = NULL;
224 char **ppmember;
225 int err = 0;
226
227 if (!user || !group) {
228 return err;
229 }
230
231 if (pwcache.user && strcmp(pwcache.user, user) == 0 && pwcache.gid != -1) {
232 /* user is cached. Use the gid in cache. */
233 gid = pwcache.gid;
234 } else {
235 pw = getpwnam(user);
236 if (NULL == pw) {
237 return err;
238 }
239 gid = pw->pw_gid;
240 }
241
242 grp = getgrnam(group);
243 if (NULL == grp) {
244 return err;
245 }
246
247 if (gid == grp->gr_gid) {
248 return 1;
249 }
250
251 ppmember = grp->gr_mem;
252 while (*ppmember) {
253 if (strcmp(*ppmember, user) == 0) {
254 err = 1;
255 break;
256 }
257 ppmember++;
258 }
259
260 return err;
261 }
262
263 /**
264 * Get the user's home. If the user name is in cache. We will return
265 * home in cache. Otherwise, use getpwnam() to get pw_dir and return.
266 *
267 * Caller should free() the returned string.
268 *
269 * @param user Which user's home to get?
270 *
271 * @return String pointer or NULL on faill
272 */
auth_pam_get_home(const char * user)273 char *auth_pam_get_home(const char *user)
274 {
275 struct passwd *pw = NULL;
276
277 if (!user) {
278 return NULL;
279 }
280
281 if (pwcache.user && strcmp(pwcache.user, user) == 0 && pwcache.home) {
282 return strdup(pwcache.home);
283 }
284
285 pw = getpwnam(user);
286 if (NULL == pw) {
287 return NULL;
288 }
289
290 if (pw->pw_dir) {
291 return strdup(pw->pw_dir);
292 }
293
294 return NULL;
295 }
296 #endif
297