]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/uam.c
The following were necessary in order to implement a Kerbers 5 UAM compatible
[netatalk.git] / etc / afpd / uam.c
1 /*
2  * $Id: uam.c,v 1.24 2003-04-16 22:45:11 samnoble Exp $
3  *
4  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif /* HAVE_UNISTD_H */
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #endif /* HAVE_FCNTL_H */
36 #include <ctype.h>
37 #include <atalk/logger.h>
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 #ifdef HAVE_DLFCN_H
42 #include <dlfcn.h>
43 #endif /* HAVE_DLFCN_H */
44
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include <netatalk/endian.h>
49 #include <atalk/asp.h>
50 #include <atalk/dsi.h>
51 #include <atalk/afp.h>
52 #include <atalk/util.h>
53
54 #include "globals.h"
55 #include "afp_config.h"
56 #include "auth.h"
57 #include "uam_auth.h"
58
59 #ifdef TRU64
60 #include <netdb.h>
61 #include <sia.h>
62 #include <siad.h>
63 #include <signal.h>
64 #endif /* TRU64 */
65
66 /* --- server uam functions -- */
67 #ifndef NO_LOAD_UAM
68 extern  int uam_setup(const char *path);
69 #endif
70
71 /* uam_load. uams must have a uam_setup function. */
72 struct uam_mod *uam_load(const char *path, const char *name)
73 {
74     char buf[MAXPATHLEN + 1], *p;
75     struct uam_mod *mod;
76     void *module;
77
78 #ifndef NO_LOAD_UAM
79     if ((module = mod_open(path)) == NULL) {
80         LOG(log_error, logtype_afpd, "uam_load(%s): failed to load: %s", name, mod_error());
81         return NULL;
82     }
83 #endif
84
85     if ((mod = (struct uam_mod *) malloc(sizeof(struct uam_mod))) == NULL) {
86         LOG(log_error, logtype_afpd, "uam_load(%s): malloc failed", name);
87         goto uam_load_fail;
88     }
89
90     strncpy(buf, name, sizeof(buf));
91     buf[sizeof(buf) - 1] = '\0';
92     if ((p = strchr(buf, '.')))
93         *p = '\0';
94
95 #ifndef NO_LOAD_UAM
96     if ((mod->uam_fcn = mod_symbol(module, buf)) == NULL) {
97         LOG(log_error, logtype_afpd, "uam_load(%s): mod_symbol error for symbol %s",
98             name,
99             buf);
100         goto uam_load_err;
101     }
102
103     if (mod->uam_fcn->uam_type != UAM_MODULE_SERVER) {
104         LOG(log_error, logtype_afpd, "uam_load(%s): attempted to load a non-server module",
105             name);
106         goto uam_load_err;
107     }
108
109     /* version check would go here */
110
111     if (!mod->uam_fcn->uam_setup ||
112             ((*mod->uam_fcn->uam_setup)(name) < 0)) {
113         LOG(log_error, logtype_afpd, "uam_load(%s): uam_setup failed", name);
114         goto uam_load_err;
115     }
116 #else
117    uam_setup(name);
118 #endif
119
120     mod->uam_module = module;
121     return mod;
122
123 uam_load_err:
124     free(mod);
125 uam_load_fail:
126     mod_close(module);
127     return NULL;
128 }
129
130 /* unload the module. we check for a cleanup function, but we don't
131  * die if one doesn't exist. however, things are likely to leak without one.
132  */
133 void uam_unload(struct uam_mod *mod)
134 {
135     if (mod->uam_fcn->uam_cleanup)
136         (*mod->uam_fcn->uam_cleanup)();
137
138 #ifndef NO_LOAD_UAM
139     mod_close(mod->uam_module);
140 #endif    
141     free(mod);
142 }
143
144 /* -- client-side uam functions -- */
145 #ifndef ATACC
146 /* set up stuff for this uam. */
147 int uam_register(const int type, const char *path, const char *name, ...)
148 {
149     va_list ap;
150     struct uam_obj *uam;
151
152     if (!name)
153         return -1;
154
155     /* see if it already exists. */
156     if ((uam = auth_uamfind(type, name, strlen(name)))) {
157         if (strcmp(uam->uam_path, path)) {
158             /* it exists, but it's not the same module. */
159             LOG(log_error, logtype_afpd, "uam_register: \"%s\" already loaded by %s",
160                 name, path);
161             return -1;
162         }
163         uam->uam_count++;
164         return 0;
165     }
166
167     /* allocate space for uam */
168     if ((uam = calloc(1, sizeof(struct uam_obj))) == NULL)
169         return -1;
170
171     uam->uam_name = name;
172     uam->uam_path = strdup(path);
173     uam->uam_count++;
174
175     va_start(ap, name);
176     switch (type) {
177     case UAM_SERVER_LOGIN_EXT: /* expect four arguments */
178         uam->u.uam_login.login = va_arg(ap, void *);
179         uam->u.uam_login.logincont = va_arg(ap, void *);
180         uam->u.uam_login.logout = va_arg(ap, void *);
181         uam->u.uam_login.login_ext = va_arg(ap, void *);
182         break;
183     
184     case UAM_SERVER_LOGIN: /* expect three arguments */
185         uam->u.uam_login.login_ext = NULL;
186         uam->u.uam_login.login = va_arg(ap, void *);
187         uam->u.uam_login.logincont = va_arg(ap, void *);
188         uam->u.uam_login.logout = va_arg(ap, void *);
189         break;
190     case UAM_SERVER_CHANGEPW: /* one argument */
191         uam->u.uam_changepw = va_arg(ap, void *);
192         break;
193     case UAM_SERVER_PRINTAUTH: /* x arguments */
194     default:
195         break;
196     }
197     va_end(ap);
198
199     /* attach to other uams */
200     if (auth_register(type, uam) < 0) {
201         free(uam->uam_path);
202         free(uam);
203         return -1;
204     }
205
206     return 0;
207 }
208 #endif
209
210 #ifdef ATACC
211 int uam_register_fn(const int type, const char *path, const char *name, void *fn1, void *fn2, 
212                      void *fn3, void *fn4)
213 {
214     va_list ap;
215     struct uam_obj *uam;
216
217     if (!name)
218         return -1;
219
220     /* see if it already exists. */
221     if ((uam = auth_uamfind(type, name, strlen(name)))) {
222         if (strcmp(uam->uam_path, path)) {
223             /* it exists, but it's not the same module. */
224             LOG(log_error, logtype_afpd, "uam_register: \"%s\" already loaded by %s",
225                 name, path);
226             return -1;
227         }
228         uam->uam_count++;
229         return 0;
230     }
231
232     /* allocate space for uam */
233     if ((uam = calloc(1, sizeof(struct uam_obj))) == NULL)
234         return -1;
235
236     uam->uam_name = name;
237     uam->uam_path = strdup(path);
238     uam->uam_count++;
239
240     switch (type) {
241     case UAM_SERVER_LOGIN_EXT: /* expect four arguments */
242         uam->u.uam_login.login_ext = fn4;
243         uam->u.uam_login.login = fn1;
244         uam->u.uam_login.logincont = fn2;
245         uam->u.uam_login.logout = fn3;
246         break;
247     case UAM_SERVER_LOGIN: /* expect three arguments */
248         uam->u.uam_login.login_ext = NULL;
249         uam->u.uam_login.login = fn1;
250         uam->u.uam_login.logincont = fn2;
251         uam->u.uam_login.logout = fn3;
252         break;
253     case UAM_SERVER_CHANGEPW: /* one argument */
254         uam->u.uam_changepw = fn1;
255         break;
256     case UAM_SERVER_PRINTAUTH: /* x arguments */
257     default:
258         break;
259     }
260
261     /* attach to other uams */
262     if (auth_register(type, uam) < 0) {
263         free(uam->uam_path);
264         free(uam);
265         return -1;
266     }
267
268     return 0;
269 }
270 #endif
271
272 void uam_unregister(const int type, const char *name)
273 {
274     struct uam_obj *uam;
275
276     if (!name)
277         return;
278
279     uam = auth_uamfind(type, name, strlen(name));
280     if (!uam || --uam->uam_count > 0)
281         return;
282
283     auth_unregister(uam);
284     free(uam->uam_path);
285     free(uam);
286 }
287
288 /* --- helper functions for plugin uams --- */
289
290 struct passwd *uam_getname(char *name, const int len)
291 {
292     struct passwd *pwent;
293     char *user;
294     int i;
295
296     if ((pwent = getpwnam(name)))
297         return pwent;
298
299 #ifndef NO_REAL_USER_NAME
300     for (i = 0; i < len; i++)
301         name[i] = tolower(name[i]);
302
303     setpwent();
304     while ((pwent = getpwent())) {
305         if ((user = strchr(pwent->pw_gecos, ',')))
306             *user = '\0';
307         user = pwent->pw_gecos;
308
309         /* check against both the gecos and the name fields. the user
310          * might have just used a different capitalization. */
311         if ((strncasecmp(user, name, len) == 0) ||
312                 (strncasecmp(pwent->pw_name, name, len) == 0)) {
313             strncpy(name, pwent->pw_name, len);
314             name[len - 1] = '\0';
315             break;
316         }
317     }
318     endpwent();
319 #endif /* ! NO_REAL_USER_NAME */
320
321     /* os x server doesn't keep anything useful if we do getpwent */
322     return pwent ? getpwnam(name) : NULL;
323 }
324
325 int uam_checkuser(const struct passwd *pwd)
326 {
327     const char *p;
328
329     if (!pwd)
330         return -1;
331
332 #ifndef DISABLE_SHELLCHECK
333         if (!pwd->pw_shell || (*pwd->pw_shell == '\0')) {
334                 LOG(log_info, logtype_afpd, "uam_checkuser: User %s does not have a shell", pwd->pw_name);
335                 return -1;
336         }
337 #endif
338
339     while ((p = getusershell())) {
340         if ( strcmp( p, pwd->pw_shell ) == 0 )
341             break;
342     }
343     endusershell();
344
345 #ifndef DISABLE_SHELLCHECK
346     if (!p) {
347         LOG(log_info, logtype_afpd, "illegal shell %s for %s", pwd->pw_shell, pwd->pw_name);
348         return -1;
349     }
350 #endif /* DISABLE_SHELLCHECK */
351
352     return 0;
353 }
354
355 /* afp-specific functions */
356 int uam_afpserver_option(void *private, const int what, void *option,
357                          int *len)
358 {
359 AFPObj *obj = private;
360     char **buf = (char **) option; /* most of the options are this */
361     int32_t result;
362     int fd;
363
364     if (!obj || !option)
365         return -1;
366
367     switch (what) {
368     case UAM_OPTION_USERNAME:
369         *buf = (void *) obj->username;
370         if (len)
371             *len = sizeof(obj->username) - 1;
372         break;
373
374     case UAM_OPTION_GUEST:
375         *buf = (void *) obj->options.guest;
376         if (len)
377             *len = strlen(obj->options.guest);
378         break;
379
380     case UAM_OPTION_PASSWDOPT:
381         if (!len)
382             return -1;
383
384         switch (*len) {
385         case UAM_PASSWD_FILENAME:
386             *buf = (void *) obj->options.passwdfile;
387             *len = strlen(obj->options.passwdfile);
388             break;
389
390         case UAM_PASSWD_MINLENGTH:
391             *((int *) option) = obj->options.passwdminlen;
392             *len = sizeof(obj->options.passwdminlen);
393             break;
394
395         case UAM_PASSWD_MAXFAIL:
396             *((int *) option) = obj->options.loginmaxfail;
397             *len = sizeof(obj->options.loginmaxfail);
398             break;
399
400         case UAM_PASSWD_EXPIRETIME: /* not implemented */
401         default:
402             return -1;
403             break;
404         }
405         break;
406
407     case UAM_OPTION_SIGNATURE:
408         *buf = (void *) (((AFPConfig *)obj->config)->signature);
409         if (len)
410             *len = 16;
411         break;
412
413     case UAM_OPTION_RANDNUM: /* returns a random number in 4-byte units. */
414         if (!len || (*len < 0) || (*len % sizeof(result)))
415             return -1;
416
417         /* construct a random number */
418         if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
419             struct timeval tv;
420             struct timezone tz;
421             char *randnum = (char *) option;
422             int i;
423
424             if (gettimeofday(&tv, &tz) < 0)
425                 return -1;
426             srandom(tv.tv_sec + (unsigned long) obj + (unsigned long) obj->handle);
427             for (i = 0; i < *len; i += sizeof(result)) {
428                 result = random();
429                 memcpy(randnum + i, &result, sizeof(result));
430             }
431         } else {
432             result = read(fd, option, *len);
433             close(fd);
434             if (result < 0)
435                 return -1;
436         }
437         break;
438
439     case UAM_OPTION_HOSTNAME:
440         *buf = (void *) obj->options.hostname;
441         if (len)
442             *len = strlen(obj->options.hostname);
443         break;
444
445     case UAM_OPTION_PROTOCOL:
446         *buf = (void *) obj->proto;
447         break;
448     case UAM_OPTION_CLIENTNAME:
449         {
450             struct DSI *dsi = obj->handle;
451             struct hostent *hp;
452
453             hp = gethostbyaddr( (char *) &dsi->client.sin_addr,
454                                 sizeof( struct in_addr ),
455                                 dsi->client.sin_family );
456             if( hp )
457                 *buf = (void *) hp->h_name;
458             else
459                 *buf = (void *) inet_ntoa( dsi->client.sin_addr );
460         }
461         break;
462     case UAM_OPTION_COOKIE:
463         /* it's up to the uam to actually store something useful here.
464          * this just passes back a handle to the cookie. the uam side
465          * needs to do something like **buf = (void *) cookie to store
466          * the cookie. */
467         *buf = (void *) &obj->uam_cookie;
468         break;
469     case UAM_OPTION_KRB5SERVICE:
470         *buf = obj->options.k5service;
471         if (len)
472             *len = strlen(obj->options.k5service);
473         break;
474     default:
475         return -1;
476         break;
477     }
478
479     return 0;
480 }
481
482 /* if we need to maintain a connection, this is how we do it.
483  * because an action pointer gets passed in, we can stream 
484  * DSI connections */
485 int uam_afp_read(void *handle, char *buf, int *buflen,
486                  int (*action)(void *, void *, const int))
487 {
488     AFPObj *obj = handle;
489     int len;
490
491     if (!obj)
492         return AFPERR_PARAM;
493
494     switch (obj->proto) {
495     case AFPPROTO_ASP:
496         if ((len = asp_wrtcont(obj->handle, buf, buflen )) < 0)
497             goto uam_afp_read_err;
498         return action(handle, buf, *buflen);
499         break;
500
501     case AFPPROTO_DSI:
502         len = dsi_writeinit(obj->handle, buf, *buflen);
503         if (!len || ((len = action(handle, buf, len)) < 0)) {
504             dsi_writeflush(obj->handle);
505             goto uam_afp_read_err;
506         }
507
508         while ((len = (dsi_write(obj->handle, buf, *buflen)))) {
509             if ((len = action(handle, buf, len)) < 0) {
510                 dsi_writeflush(obj->handle);
511                 goto uam_afp_read_err;
512             }
513         }
514         break;
515     }
516     return 0;
517
518 uam_afp_read_err:
519     *buflen = 0;
520     return len;
521 }
522
523 #ifdef TRU64
524 void uam_afp_getcmdline( int *ac, char ***av )
525 {
526     afp_get_cmdline( ac, av );
527 }
528
529 int uam_sia_validate_user(sia_collect_func_t * collect, int argc, char **argv,
530                          char *hostname, char *username, char *tty,
531                          int colinput, char *gssapi, char *passphrase)
532 /* A clone of the Tru64 system function sia_validate_user() that calls
533  * sia_ses_authent() rather than sia_ses_reauthent()   
534  * Added extra code to take into account suspected SIA bug whereby it clobbers
535  * the signal handler on SIGALRM (tickle) installed by Netatalk/afpd
536  */
537 {
538        SIAENTITY *entity = NULL;
539        struct sigaction act;
540        int rc;
541
542        if ((rc=sia_ses_init(&entity, argc, argv, hostname, username, tty,
543                             colinput, gssapi)) != SIASUCCESS) {
544                LOG(log_error, logtype_afpd, "cannot initialise SIA");
545                return SIAFAIL;
546        }
547
548        /* save old action for restoration later */
549        if (sigaction(SIGALRM, NULL, &act))
550                LOG(log_error, logtype_afpd, "cannot save SIGALRM handler");
551
552        if ((rc=sia_ses_authent(collect, passphrase, entity)) != SIASUCCESS) {
553                /* restore old action after clobbering by sia_ses_authent() */
554                if (sigaction(SIGALRM, &act, NULL))
555                        LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler");
556
557                LOG(log_info, logtype_afpd, "unsuccessful login for %s",
558 (hostname?hostname:"(null)"));
559                return SIAFAIL;
560        }
561        LOG(log_info, logtype_afpd, "successful login for %s",
562 (hostname?hostname:"(null)"));
563
564        /* restore old action after clobbering by sia_ses_authent() */   
565        if (sigaction(SIGALRM, &act, NULL))
566                LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler");
567
568        sia_ses_release(&entity);
569
570        return SIASUCCESS;
571 }
572 #endif /* TRU64 */
573
574 /* --- papd-specific functions (just placeholders) --- */
575 void append(void *pf, char *data, int len)
576 {
577     return;
578 }