1 /*
2 ** Copyright 1998 - 2012 Double Precision, Inc.  See COPYING for
3 ** distribution information.
4 */
5 
6 #if	HAVE_CONFIG_H
7 #include	"courier_auth_config.h"
8 #endif
9 #include	<stdio.h>
10 #include	<stdlib.h>
11 #include	<string.h>
12 #include	<signal.h>
13 #include	<errno.h>
14 #if	HAVE_UNISTD_H
15 #include	<unistd.h>
16 #endif
17 #include	"auth.h"
18 #include	"authwait.h"
19 #include	"courierauthstaticlist.h"
20 #include	"courierauthdebug.h"
21 
22 #if	HAVE_SECURITY_PAM_APPL_H
23 #include	<security/pam_appl.h>
24 #endif
25 
26 #if	HAVE_PAM_PAM_APPL_H
27 #include	<Pam/pam_appl.h>
28 #endif
29 
30 extern char tcpremoteip[];
31 
32 static const char *pam_username, *pam_password, *pam_service;
33 
34 extern void auth_pwd_enumerate( void(*cb_func)(const char *name,
35 					       uid_t uid,
36 					       gid_t gid,
37 					       const char *homedir,
38 					       const char *maildir,
39 					       const char *options,
40 					       void *void_arg),
41 				void *void_arg);
42 
pam_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)43 static int pam_conv(int num_msg, const struct pam_message **msg,
44                     struct pam_response **resp, void *appdata_ptr)
45 {
46 int i = 0;
47 struct pam_response *repl = NULL;
48 
49 	repl = malloc(sizeof(struct pam_response) * num_msg);
50 	if (!repl) return PAM_CONV_ERR;
51 
52 	for (i=0; i<num_msg; i++)
53 		switch (msg[i]->msg_style) {
54 		case PAM_PROMPT_ECHO_ON:
55 			repl[i].resp_retcode = PAM_SUCCESS;
56 			repl[i].resp = strdup(pam_username);
57 			if (!repl[i].resp)
58 			{
59 				perror("strdup");
60 				exit(1);
61 			}
62 			break;
63 		case PAM_PROMPT_ECHO_OFF:
64 			repl[i].resp_retcode = PAM_SUCCESS;
65 			repl[i].resp = strdup(pam_password);
66 			if (!repl[i].resp)
67 			{
68 				perror("strdup");
69 				exit(1);
70 			}
71 			break;
72 		case PAM_TEXT_INFO:
73 		case PAM_ERROR_MSG:
74 			if (write(2, msg[i]->msg, strlen(msg[i]->msg)) < 0 ||
75 			    write(2, "\n", 1) < 0)
76 				; /* ignore gcc warning */
77 
78 			repl[i].resp_retcode = PAM_SUCCESS;
79 			repl[i].resp = NULL;
80 			break;
81 		default:
82 			free (repl);
83 			return PAM_CONV_ERR;
84 		}
85 
86 	*resp=repl;
87 	return PAM_SUCCESS;
88 }
89 
90 static struct pam_conv conv = {
91           pam_conv,
92           NULL
93       };
94 
dopam(pam_handle_t ** pamh,int * started)95 static int dopam(pam_handle_t **pamh, int *started)
96 {
97 	int	retval;
98 
99 	DPRINTF("pam_service=%s, pam_username=%s",
100 		pam_service ? pam_service : "<null>",
101 		pam_username ? pam_username : "<null>");
102 
103 	*started=1;
104 
105 	retval=pam_start(pam_service, pam_username, &conv, pamh);
106 	if (retval != PAM_SUCCESS)
107 	{
108 		DPRINTF("pam_start failed, result %d [Hint: bad PAM configuration?]", retval);
109 		*started=0;
110 	}
111 
112 #ifdef PAM_RHOST
113 	if (retval == PAM_SUCCESS && tcpremoteip[0])
114 	{
115 		retval=pam_set_item(*pamh, PAM_RHOST, tcpremoteip);
116 		if (retval != PAM_SUCCESS)
117 		{
118 			DPRINTF("pam_set_item(PAM_RHOST) failed, result %d",
119 				retval);
120 		}
121 	}
122 #endif
123 #if 0
124 	if (retval == PAM_SUCCESS)
125 	{
126 		retval=pam_set_item(*pamh, PAM_AUTHTOK, pam_password);
127 		if (retval != PAM_SUCCESS)
128 		{
129 			DPRINTF("pam_set_item failed, result %d", retval);
130 		}
131 	}
132 #endif
133 
134 	if (retval == PAM_SUCCESS)
135 	{
136 		retval=pam_authenticate(*pamh, 0);
137 		if (retval != PAM_SUCCESS)
138 		{
139 			DPRINTF("pam_authenticate failed, result %d", retval);
140 		}
141 	}
142 
143 #if 0
144 
145 #if	HAVE_PAM_SETCRED
146 	if (retval == PAM_SUCCESS)
147 	{
148 		retval=pam_setcred(*pamh, PAM_ESTABLISH_CRED);
149 		if (retval != PAM_SUCCESS)
150 		{
151 			DPRINTF("pam_setcred failed, result %d", retval);
152 		}
153 	}
154 #endif
155 #endif
156 
157 	if (retval == PAM_SUCCESS)
158 	{
159 		retval=pam_acct_mgmt(*pamh, 0);
160 		if (retval != PAM_SUCCESS)
161 		{
162 			DPRINTF("pam_acct_mgmt failed, result %d", retval);
163 		}
164 	}
165 	if (retval == PAM_SUCCESS)
166 	{
167 		DPRINTF("dopam successful");
168 	}
169 	return (retval);
170 }
171 
172 struct callback_info {
173 	int (*callback_func)(struct authinfo *, void *);
174 	void *callback_arg;
175 	} ;
176 
callback_pam(struct authinfo * a,void * argptr)177 static int callback_pam(struct authinfo *a, void *argptr)
178 {
179 struct callback_info *ci=(struct callback_info *)argptr;
180 pam_handle_t	*pamh=NULL;
181 int	pipefd[2];
182 int	retval;
183 pid_t	p;
184 int	waitstat;
185 char	*s;
186 char	buf[1];
187 
188 
189 	a->clearpasswd=pam_password;
190 	s=strdup(a->sysusername);
191 	if (!s)
192 	{
193 		perror("malloc");
194 		return (1);
195 	}
196 
197 
198 /*
199 **	OK, in order to transparently support PAM sessions inside this
200 **	authentication module, what we need to do is to fork(), and let
201 **	the child run in its parent place.  Once the child process exits,
202 **	the parent calls pam_end_session, and clears the PAM library.
203 **
204 **	This means that upon return from auth_pam(), your process ID
205 **	might've changed!!!
206 **
207 **	However, if the authentication fails, we can simply exit, without
208 **	running as a child process.
209 **
210 **	Additionally, the PAM library might allocate some resources that
211 **	authenticated clients should not access.  Therefore, we fork
212 **	*before* we try to authenticate.  If the authentication succeeds,
213 **	the child process will run in the parent's place.  The child
214 **	process waits until the parent tells it whether the authentication
215 **	worked.  If it worked, the child keeps running.  If not, the child
216 **	exits, which the parent waits for.
217 **
218 **	The authentication status is communicated to the child process via
219 **	a pipe.
220 */
221 	if (pipe(pipefd) < 0)
222 	{
223 		perror("pipe");
224 		free(s);
225 		return (1);
226 	}
227 
228 	if ((p=fork()) == -1)
229 	{
230 		perror("fork");
231 		free(s);
232 		return (1);
233 	}
234 
235 	if (p == 0)
236 	{
237 		int started;
238 
239 		close(pipefd[0]);
240 		retval=dopam(&pamh, &started);
241 		if (retval == PAM_SUCCESS)
242 			if (write(pipefd[1], "", 1) < 0)
243 				; /* ignore gcc warning */
244 		close(pipefd[1]);
245 
246 		if (started)
247 			pam_end(pamh, retval);
248 		_exit(0);
249 	}
250 
251 
252 	close(pipefd[1]);
253 	while (wait(&waitstat) != p)
254 		;
255 	if (read(pipefd[0], buf, 1) > 0)
256 	{
257 		int rc;
258 
259 		close(pipefd[0]);
260 		a->address=s;
261 		rc=(*ci->callback_func)(a, ci->callback_arg);
262 		free(s);
263 		return rc;
264 	}
265 	close(pipefd[0]);
266 	free(s);
267 	errno=EPERM;
268 	return (-1);
269 }
270 
271 extern int auth_pam_pre(const char *userid, const char *service,
272         int (*callback)(struct authinfo *, void *),
273                         void *arg);
274 
auth_pam(const char * service,const char * type,char * authdata,int (* callback_func)(struct authinfo *,void *),void * callback_arg)275 int auth_pam(const char *service, const char *type, char *authdata,
276 	     int (*callback_func)(struct authinfo *, void *),
277 	     void *callback_arg)
278 {
279 	struct	callback_info	ci;
280 
281 	if (strcmp(type, AUTHTYPE_LOGIN))
282 	{
283 		DPRINTF("authpam only handles authtype=" AUTHTYPE_LOGIN);
284 		errno=EPERM;
285 		return (-1);
286 	}
287 
288 	if ((pam_username=strtok(authdata, "\n")) == 0 ||
289 		(pam_password=strtok(0, "\n")) == 0)
290 	{
291 		DPRINTF("incomplete username or missing password");
292 		errno=EPERM;
293 		return (-1);
294 	}
295 
296 	pam_service=service;
297 
298 	ci.callback_func=callback_func;
299 	ci.callback_arg=callback_arg;
300         return auth_pam_pre(pam_username, service, &callback_pam, &ci);
301 }
302 
auth_pam_cleanup()303 static void auth_pam_cleanup()
304 {
305 }
306 
307 static struct authstaticinfo authpam_info={
308 	"authpam",
309 	auth_pam,
310 	auth_pam_pre,
311 	auth_pam_cleanup,
312 	auth_syspasswd,
313 	auth_pam_cleanup,
314 	auth_pwd_enumerate};
315 
316 
courier_authpam_init()317 struct authstaticinfo *courier_authpam_init()
318 {
319 	return &authpam_info;
320 }
321