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