2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * I have decided that I don't like PAM. Maybe I'm inexperienced, so
21 * please correct me if I'm wrong, but is it not so that
22 * pam_authenticate blocks until the user has fully authenticated
23 * herself? That isn't very good in a program that wants to do other
24 * things at the same time. In my mind, pam_authenticate should return
25 * with a conversation struct every time it wants data.
27 * My solution here, for now, is to use the ucontext context switching
28 * functions to get back and forth from the conversation
29 * function. Ugly? Yes indeed, it most certainly is, but what am I to
30 * do, then? If there actually is a good way to do this that is built
31 * into PAM, _please_, do mail me about it.
38 #include <security/pam_appl.h>
53 ucontext_t mainctxt, pamctxt;
55 volatile int validctxt;
56 volatile int convdone, converr;
57 volatile char *passdata;
60 static int pamconv(int nmsg, const struct pam_message **msg, struct pam_response **resp, struct authhandle *auth)
65 data = auth->mechdata;
66 *resp = smalloc(sizeof(**resp) * nmsg);
67 for(i = 0; i < nmsg; i++)
69 switch(msg[i]->msg_style)
71 case PAM_PROMPT_ECHO_OFF:
72 auth->prompt = AUTH_PR_NOECHO;
74 case PAM_PROMPT_ECHO_ON:
75 auth->prompt = AUTH_PR_ECHO;
78 auth->prompt = AUTH_PR_ERROR;
81 auth->prompt = AUTH_PR_INFO;
84 if(auth->text != NULL)
86 if((auth->text = icmbstowcs((char *)msg[i]->msg, NULL)) == NULL)
88 flog(LOG_ERR, "could not convert PAM error %s into wcs: %s", msg[i]->msg, strerror(errno));
93 if(swapcontext(&data->pamctxt, &data->mainctxt))
95 flog(LOG_CRIT, "could not swap context in PAM conversation: %s", strerror(errno));
104 (*resp)[i].resp = sstrdup("");
105 (*resp)[i].resp_retcode = PAM_SUCCESS;
107 return(PAM_CONV_ERR);
109 switch(msg[i]->msg_style)
111 case PAM_PROMPT_ECHO_OFF:
112 case PAM_PROMPT_ECHO_ON:
113 (*resp)[i].resp = sstrdup((char *)data->passdata);
114 memset((void *)data->passdata, 0, strlen((char *)data->passdata));
115 (*resp)[i].resp_retcode = PAM_SUCCESS;
122 static void releasepam(struct pamdata *data)
124 if(data->pamh != NULL)
129 if(swapcontext(&data->mainctxt, &data->pamctxt))
131 flog(LOG_CRIT, "could not switch back to PAM context while releasing: %s", strerror(errno));
135 pam_end(data->pamh, data->pamret);
137 if(data->pamstack != NULL)
138 free(data->pamstack);
142 static void release(struct authhandle *auth)
144 releasepam((struct pamdata *)auth->mechdata);
147 static struct pamdata *newpamdata(void)
151 new = smalloc(sizeof(*new));
153 new->pamret = PAM_SUCCESS;
154 new->pamstack = NULL;
160 static int inithandle(struct authhandle *auth, char *username)
163 struct pamdata *data;
164 struct pam_conv conv;
167 conv.conv = (int (*)(int, const struct pam_message **, struct pam_response **, void *))pamconv;
168 conv.appdata_ptr = auth;
169 if((buf = icwcstombs(confgetstr("auth", "pamserv"), NULL)) == NULL)
171 flog(LOG_ERR, "could not initialize pam since auth.pamserv cannot be translated into the current locale: %s", strerror(errno));
175 if((data->pamret = pam_start(buf, username, &conv, &data->pamh)) != PAM_SUCCESS)
177 flog(LOG_CRIT, "could not pam_start: %s", pam_strerror(NULL, data->pamret));
180 errno = ENOTSUP; /* XXX */
184 auth->mechdata = data;
188 static void pamauththread(struct authhandle *auth)
190 struct pamdata *data;
192 data = (struct pamdata *)auth->mechdata;
194 data->pamret = pam_authenticate(data->pamh, 0);
198 static int pamauth(struct authhandle *auth, char *passdata)
200 struct pamdata *data;
202 data = auth->mechdata;
205 if(getcontext(&data->pamctxt))
207 flog(LOG_CRIT, "could not get context: %s", strerror(errno));
210 data->pamctxt.uc_link = &data->mainctxt;
211 if(data->pamstack == NULL)
212 data->pamstack = smalloc(65536);
213 data->pamctxt.uc_stack.ss_sp = data->pamstack;
214 data->pamctxt.uc_stack.ss_size = 65536;
215 makecontext(&data->pamctxt, (void (*)(void))pamauththread, 1, auth);
216 if(swapcontext(&data->mainctxt, &data->pamctxt))
218 flog(LOG_CRIT, "Could not switch to PAM context: %s", strerror(errno));
223 if(data->pamret == PAM_AUTHINFO_UNAVAIL)
225 else if(data->pamret == PAM_SUCCESS)
226 return(AUTH_SUCCESS);
232 data->passdata = passdata;
233 if(swapcontext(&data->mainctxt, &data->pamctxt))
235 flog(LOG_CRIT, "could not switch back to PAM context: %s", strerror(errno));
240 if(data->pamret == PAM_AUTHINFO_UNAVAIL)
242 else if(data->pamret == PAM_SUCCESS)
243 return(AUTH_SUCCESS);
251 static int renewcred(struct authhandle *auth)
253 struct pamdata *data;
255 data = auth->mechdata;
256 if(data->pamh == NULL)
257 return(AUTH_SUCCESS);
258 data->pamret = pam_setcred(data->pamh, PAM_REFRESH_CRED);
259 if(data->pamret != PAM_SUCCESS)
261 flog(LOG_INFO, "could not refresh credentials: %s", pam_strerror(data->pamh, data->pamret));
264 return(AUTH_SUCCESS);
267 static int opensess(struct authhandle *auth)
269 struct pamdata *data;
272 data = auth->mechdata;
273 if(data->pamh == NULL)
275 flog(LOG_ERR, "bug: in auth-pam.c:opensess: called with NULL pamh");
278 data->pamret = pam_setcred(data->pamh, PAM_ESTABLISH_CRED);
279 if(data->pamret != PAM_SUCCESS)
281 flog(LOG_INFO, "could not establish credentials: %s", pam_strerror(data->pamh, data->pamret));
284 data->pamret = pam_open_session(data->pamh, 0);
285 if(data->pamret != PAM_SUCCESS)
287 flog(LOG_INFO, "could not open session: %s", pam_strerror(data->pamh, data->pamret));
290 for(envp = pam_getenvlist(data->pamh); *envp; envp++)
292 return(AUTH_SUCCESS);
295 static int closesess(struct authhandle *auth)
298 struct pamdata *data;
300 data = auth->mechdata;
301 if(data->pamh == NULL)
303 flog(LOG_ERR, "bug: in auth-pam.c:closesess: called with NULL pamh");
307 data->pamret = pam_close_session(data->pamh, 0);
308 if(data->pamret != PAM_SUCCESS)
310 flog(LOG_INFO, "could not open session: %s", pam_strerror(data->pamh, data->pamret));
313 data->pamret = pam_setcred(data->pamh, PAM_DELETE_CRED);
314 if(data->pamret != PAM_SUCCESS)
316 flog(LOG_INFO, "could not establish credentials: %s", pam_strerror(data->pamh, data->pamret));
322 struct authmech authmech_pam =
324 .inithandle = inithandle,
326 .authenticate = pamauth,
327 .renewcred = renewcred,
328 .opensess = opensess,
329 .closesess = closesess,