]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_pam.c
uams CVS Id tag addition, uams_dhx_pam auth module works again, new spec file for...
[netatalk.git] / etc / uams / uams_pam.c
1 /*
2  * $Id: uams_pam.c,v 1.6 2001-02-27 17:07:43 rufustfirefly Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
6  * All Rights Reserved.  See COPYRIGHT.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #ifdef USE_PAM
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <syslog.h>
19
20 #include <security/pam_appl.h>
21
22 #include <atalk/afp.h>
23 #include <atalk/uam.h>
24
25 #define PASSWDLEN 8
26
27 /* Static variables used to communicate between the conversation function
28  * and the server_login function
29  */
30 static pam_handle_t *pamh = NULL; 
31 static char *hostname;
32 static char *PAM_username;
33 static char *PAM_password;
34
35 /* PAM conversation function
36  * Here we assume (for now, at least) that echo on means login name, and
37  * echo off means password.
38  */
39 static int PAM_conv (int num_msg,
40                      const struct pam_message **msg,
41                      struct pam_response **resp,
42                      void *appdata_ptr) 
43 {
44   struct pam_response *reply;
45   int count;
46   
47 #define COPY_STRING(s) (s) ? strdup(s) : NULL
48   
49   if (num_msg < 1)
50     return PAM_CONV_ERR;
51
52   reply = (struct pam_response *) 
53     calloc(num_msg, sizeof(struct pam_response));
54
55   if (!reply)
56     return PAM_CONV_ERR;
57
58   for (count = 0; count < num_msg; count++) {
59     char *string = NULL;
60
61     switch (msg[count]->msg_style) {
62     case PAM_PROMPT_ECHO_ON:
63       if (!(string = COPY_STRING(PAM_username)))
64         goto pam_fail_conv;
65       break;
66     case PAM_PROMPT_ECHO_OFF:
67       if (!(string = COPY_STRING(PAM_password)))
68         goto pam_fail_conv;
69       break;
70     case PAM_TEXT_INFO:
71 #ifdef PAM_BINARY_PROMPT
72     case PAM_BINARY_PROMPT:
73 #endif
74       /* ignore it... */
75       break;
76     case PAM_ERROR_MSG:
77     default:
78       goto pam_fail_conv;
79     }
80
81     if (string) {  
82       reply[count].resp_retcode = 0;
83       reply[count].resp = string;
84       string = NULL;
85     }
86   }
87
88   *resp = reply;
89   return PAM_SUCCESS;
90
91 pam_fail_conv:
92   for (count = 0; count < num_msg; count++) {
93     if (!reply[count].resp)
94       continue;
95     switch (msg[count]->msg_style) {
96     case PAM_PROMPT_ECHO_OFF:
97     case PAM_PROMPT_ECHO_ON:
98       free(reply[count].resp);
99       break;      
100     }
101   }
102   free(reply);
103   return PAM_CONV_ERR;
104 }
105
106 static struct pam_conv PAM_conversation = {
107   &PAM_conv,
108   NULL
109 };
110
111
112 /* cleartxt login */
113 static int pam_login(void *obj, struct passwd **uam_pwd,
114                      char *ibuf, int ibuflen,
115                      char *rbuf, int *rbuflen)
116 {
117     struct passwd *pwd;
118     char *username; 
119     int err, len, ulen, PAM_error;
120
121     *rbuflen = 0;
122
123     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
124                              (void *) &username, &ulen) < 0)
125       return AFPERR_MISC;
126
127     if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
128                              (void *) &hostname, NULL) < 0)
129       return AFPERR_MISC;
130
131     len = (unsigned char) *ibuf++;
132     if ( len > ulen ) {
133         return( AFPERR_PARAM );
134     }
135
136     memcpy(username, ibuf, len );
137     ibuf += len;
138     username[ len ] = '\0';
139     if ((unsigned long) ibuf & 1)  /* pad character */
140       ++ibuf;
141     ibuf[ PASSWDLEN ] = '\0';
142
143     if (( pwd = uam_getname(username, ulen)) == NULL ) {
144         return AFPERR_PARAM;
145     }
146
147     syslog(LOG_INFO, "cleartext login: %s", username);
148     PAM_username = username;
149     PAM_password = ibuf; /* Set these things up for the conv function */
150
151     err = AFPERR_NOTAUTH;
152     PAM_error = pam_start("netatalk", username, &PAM_conversation,
153                           &pamh);
154     if (PAM_error != PAM_SUCCESS)
155       goto login_err;
156
157     pam_set_item(pamh, PAM_TTY, "afpd");
158     pam_set_item(pamh, PAM_RHOST, hostname);
159     /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */
160     PAM_error = pam_authenticate(pamh,0);
161     if (PAM_error != PAM_SUCCESS) {
162       if (PAM_error == PAM_MAXTRIES) 
163         err = AFPERR_PWDEXPR;
164       goto login_err;
165     }      
166
167     PAM_error = pam_acct_mgmt(pamh, 0);
168     if (PAM_error != PAM_SUCCESS) {
169       if (PAM_error == PAM_ACCT_EXPIRED)
170         err = AFPERR_PWDEXPR;
171 #ifdef PAM_AUTHTOKEN_REQD
172       else if (PAM_error == PAM_AUTHTOKEN_REQD) 
173         err = AFPERR_PWDCHNG;
174 #endif
175       goto login_err;
176     }
177
178 #ifndef PAM_CRED_ESTABLISH
179 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
180 #endif
181     PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
182     if (PAM_error != PAM_SUCCESS)
183       goto login_err;
184
185     PAM_error = pam_open_session(pamh, 0);
186     if (PAM_error != PAM_SUCCESS)
187       goto login_err;
188
189     *uam_pwd = pwd;
190     return AFP_OK;
191
192 login_err:
193     pam_end(pamh, PAM_error);
194     pamh = NULL;
195     return err;
196 }
197
198 /* logout */
199 static void pam_logout() {
200     pam_close_session(pamh, 0);
201     pam_end(pamh, 0);
202     pamh = NULL;
203 }
204
205 /* change passwd */
206 static int pam_changepw(void *obj, char *username,
207                         struct passwd *pwd, char *ibuf, int ibuflen,
208                         char *rbuf, int *rbuflen)
209 {
210     char pw[PASSWDLEN + 1];
211     pam_handle_t *lpamh;
212     uid_t uid;
213     int PAM_error;
214
215     /* old password */
216     memcpy(pw, ibuf, PASSWDLEN);
217     memset(ibuf, 0, PASSWDLEN);
218     pw[PASSWDLEN] = '\0';
219
220     /* let's do a quick check for the same password */
221     if (memcmp(pw, ibuf + PASSWDLEN, PASSWDLEN) == 0)
222       return AFPERR_PWDSAME;
223
224     /* Set these things up for the conv function */
225     PAM_username = username;
226     PAM_password = pw; 
227
228     PAM_error = pam_start("netatalk", username, &PAM_conversation,
229                           &lpamh);
230     if (PAM_error != PAM_SUCCESS) 
231       return AFPERR_PARAM;
232     pam_set_item(lpamh, PAM_TTY, "afpd");
233     pam_set_item(lpamh, PAM_RHOST, hostname);
234
235     /* we might need to do this as root */
236     uid = geteuid();
237     seteuid(0);
238     PAM_error = pam_authenticate(lpamh,0);
239     if (PAM_error != PAM_SUCCESS) {
240       seteuid(uid);
241       pam_end(lpamh, PAM_error);
242       return AFPERR_NOTAUTH;
243     }
244
245     /* new password */
246     ibuf += PASSWDLEN;
247     PAM_password = ibuf;
248     ibuf[PASSWDLEN] = '\0';
249     
250     /* this really does need to be done as root */
251     PAM_error = pam_chauthtok(lpamh, 0);
252     seteuid(uid); /* un-root ourselves. */
253     memset(pw, 0, PASSWDLEN);
254     memset(ibuf, 0, PASSWDLEN);
255     if (PAM_error != PAM_SUCCESS) {
256       pam_end(lpamh, PAM_error);
257       return AFPERR_ACCESS;
258     }
259
260     pam_end(lpamh, 0);
261     return AFP_OK;
262 }
263
264
265 /* Printer ClearTxtUAM login */
266 int pam_printer(start, stop, username, out)
267         char    *start, *stop, *username;
268         struct papfile  *out;
269 {
270     int PAM_error;
271     char        *data, *p, *q;
272     char        password[PASSWDLEN + 1] = "\0";
273     static const char *loginok = "0\r";
274
275     data = (char *)malloc(stop - start + 1);
276     strncpy(data, start, stop - start + 1);
277
278     /* We are looking for the following format in data:
279      * (username) (password)
280      *
281      * Let's hope username doesn't contain ") ("!
282      */
283
284     /* Parse input for username in () */
285     if ((p = strchr(data, '(' )) == NULL) {
286         syslog(LOG_INFO,"Bad Login ClearTxtUAM: username not found in string");
287         free(data);
288         return(-1);
289     }
290     p++;
291     if ((q = strstr(data, ") (" )) == NULL) {
292         syslog(LOG_INFO,"Bad Login ClearTxtUAM: username not found in string");
293         free(data);
294         return(-1);
295     }
296     strncpy(username, p, q - p);
297
298     /* Parse input for password in next () */
299     p = q + 3;
300     if ((q = strrchr(data, ')' )) == NULL) {
301         syslog(LOG_INFO,"Bad Login ClearTxtUAM: password not found in string");
302         free(data);
303         return(-1);
304     }
305     strncpy(password, p, q - p);
306
307     /* Done copying username and password, clean up */
308     free(data);
309
310     PAM_username = username;
311     PAM_password = password;
312
313     PAM_error = pam_start("netatalk", username, &PAM_conversation,
314                           &pamh);
315     if (PAM_error != PAM_SUCCESS) {
316         syslog(LOG_INFO, "Bad Login ClearTxtUAM: %s: %s", 
317                         username, pam_strerror(pamh, PAM_error));
318         pam_end(pamh, PAM_error);
319         pamh = NULL;
320         return(-1);
321     }
322
323     pam_set_item(pamh, PAM_TTY, "papd");
324     pam_set_item(pamh, PAM_RHOST, hostname);
325     PAM_error = pam_authenticate(pamh,0);
326     if (PAM_error != PAM_SUCCESS) {
327         syslog(LOG_INFO, "Bad Login ClearTxtUAM: %s: %s", 
328                         username, pam_strerror(pamh, PAM_error));
329         pam_end(pamh, PAM_error);
330         pamh = NULL;
331         return(-1);
332     }      
333
334     PAM_error = pam_acct_mgmt(pamh, 0);
335     if (PAM_error != PAM_SUCCESS) {
336         syslog(LOG_INFO, "Bad Login ClearTxtUAM: %s: %s", 
337                         username, pam_strerror(pamh, PAM_error));
338         pam_end(pamh, PAM_error);
339         pamh = NULL;
340         return(-1);
341     }
342
343     PAM_error = pam_open_session(pamh, 0);
344     if (PAM_error != PAM_SUCCESS) {
345         syslog(LOG_INFO, "Bad Login ClearTxtUAM: %s: %s", 
346                         username, pam_strerror(pamh, PAM_error));
347         pam_end(pamh, PAM_error);
348         pamh = NULL;
349         return(-1);
350     }
351
352     /* Login successful, but no need to hang onto it,
353        so logout immediately */
354     append(out, loginok, strlen(loginok));
355     syslog(LOG_INFO, "Login ClearTxtUAM: %s", username);
356     pam_close_session(pamh, 0);
357     pam_end(pamh, 0);
358     pamh = NULL;
359
360     return(0);
361 }
362
363
364 static int uam_setup(const char *path)
365 {
366   if (uam_register(UAM_SERVER_LOGIN, path, "Cleartxt Passwrd", 
367                    pam_login, NULL, pam_logout) < 0)
368         return -1;
369
370   if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd",
371                    pam_changepw) < 0) {
372         uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
373         return -1;
374   }
375
376   if (uam_register(UAM_SERVER_PRINTAUTH, path, "ClearTxtUAM",
377                    pam_printer) < 0) {
378         return -1;
379   }
380
381   return 0;
382 }
383
384 static void uam_cleanup(void)
385 {
386   uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
387   uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd");
388   uam_unregister(UAM_SERVER_PRINTAUTH, "ClearTxtUAM");
389 }
390
391 UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
392   UAM_MODULE_SERVER,
393   UAM_MODULE_VERSION,
394   uam_setup, uam_cleanup
395 };
396
397 #endif /* USE_PAM */