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