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