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