]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/uam.c
Add option 'ad domain'
[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 #include <atalk/volume.h>
34 #include <atalk/bstrlib.h>
35
36 #include "afp_config.h"
37 #include "auth.h"
38 #include "uam_auth.h"
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.addomain || (obj->options.ntdomain && obj->options.ntseparator)) {
214         /* FIXME What about charset ? */
215         bstring princ;
216         if (obj->options.addomain)
217             princ = bformat("%s@%s", name, obj->options.addomain);
218         else
219             princ = bformat("%s%s%s", obj->options.ntdomain, obj->options.ntseparator, name);
220         pwent = getpwnam(bdata(princ));
221         bdestroy(princ);
222
223         if (pwent) {
224             int len = strlen(pwent->pw_name);              
225             if (len < MAXUSERLEN) {
226                 strncpy(name,pwent->pw_name, MAXUSERLEN);  
227             } else {
228                 LOG(log_error, logtype_uams, "The name '%s' is longer than %d", pwent->pw_name, MAXUSERLEN);
229             }
230             return pwent;
231         }
232     }
233 #ifndef NO_REAL_USER_NAME
234
235     if ( (size_t) -1 == (namelen = convert_string((utf8_encoding(obj))?CH_UTF8_MAC:obj->options.maccharset,
236                                 CH_UCS2, name, -1, username, sizeof(username))))
237         return NULL;
238
239     setpwent();
240     while ((pwent = getpwent())) {
241         if ((p = strchr(pwent->pw_gecos, ',')))
242             *p = '\0';
243
244         gecoslen = convert_string(obj->options.unixcharset, CH_UCS2, 
245                                 pwent->pw_gecos, -1, user, sizeof(username));
246         pwnamelen = convert_string(obj->options.unixcharset, CH_UCS2, 
247                                 pwent->pw_name, -1, pwname, sizeof(username));
248         if ((size_t)-1 == gecoslen && (size_t)-1 == pwnamelen)
249                 continue;
250
251
252         /* check against both the gecos and the name fields. the user
253          * might have just used a different capitalization. */
254
255         if ( (namelen == gecoslen && strncasecmp_w((ucs2_t*)user, (ucs2_t*)username, len) == 0) || 
256                 ( namelen == pwnamelen && strncasecmp_w ( (ucs2_t*) pwname, (ucs2_t*) username, len) == 0)) {
257             strlcpy(name, pwent->pw_name, len);
258             break;
259         }
260     }
261     endpwent();
262 #endif /* ! NO_REAL_USER_NAME */
263
264     /* os x server doesn't keep anything useful if we do getpwent */
265     return pwent ? getpwnam(name) : NULL;
266 }
267
268 int uam_checkuser(const struct passwd *pwd)
269 {
270     const char *p;
271
272     if (!pwd)
273         return -1;
274
275 #ifndef DISABLE_SHELLCHECK
276         if (!pwd->pw_shell || (*pwd->pw_shell == '\0')) {
277                 LOG(log_info, logtype_afpd, "uam_checkuser: User %s does not have a shell", pwd->pw_name);
278                 return -1;
279         }
280
281     while ((p = getusershell())) {
282         if ( strcmp( p, pwd->pw_shell ) == 0 )
283             break;
284     }
285     endusershell();
286
287     if (!p) {
288         LOG(log_info, logtype_afpd, "illegal shell %s for %s", pwd->pw_shell, pwd->pw_name);
289         return -1;
290     }
291 #endif /* DISABLE_SHELLCHECK */
292
293     return 0;
294 }
295
296 int uam_random_string (AFPObj *obj, char *buf, int len)
297 {
298     uint32_t result;
299     int ret;
300     int fd;
301
302     if ( (len <= 0) || (len % sizeof(result)))
303             return -1;
304
305     /* construct a random number */
306     if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
307         struct timeval tv;
308         struct timezone tz;
309         int i;
310
311         if (gettimeofday(&tv, &tz) < 0)
312             return -1;
313         srandom(tv.tv_sec + (unsigned long) obj + (unsigned long) obj->dsi);
314         for (i = 0; i < len; i += sizeof(result)) {
315             result = random();
316             memcpy(buf + i, &result, sizeof(result));
317         }
318     } else {
319         ret = read(fd, buf, len);
320         close(fd);
321         if (ret <= 0)
322             return -1;
323     }
324     return 0;
325 }
326
327 /* afp-specific functions */
328 int uam_afpserver_option(void *private, const int what, void *option,
329                          size_t *len)
330 {
331     AFPObj *obj = private;
332     const char **buf = (const char **) option; /* most of the options are this */
333     struct session_info **sinfo = (struct session_info **) option;
334
335     if (!obj || !option)
336         return -1;
337
338     switch (what) {
339     case UAM_OPTION_USERNAME:
340         *buf = &(obj->username[0]);
341         if (len)
342             *len = sizeof(obj->username) - 1;
343         break;
344
345     case UAM_OPTION_GUEST:
346         *buf = obj->options.guest;
347         if (len)
348             *len = strlen(obj->options.guest);
349         break;
350
351     case UAM_OPTION_PASSWDOPT:
352         if (!len)
353             return -1;
354
355         switch (*len) {
356         case UAM_PASSWD_FILENAME:
357             *buf = obj->options.passwdfile;
358             *len = strlen(obj->options.passwdfile);
359             break;
360
361         case UAM_PASSWD_MINLENGTH:
362             *((int *) option) = obj->options.passwdminlen;
363             *len = sizeof(obj->options.passwdminlen);
364             break;
365
366         case UAM_PASSWD_EXPIRETIME: /* not implemented */
367         default:
368             return -1;
369             break;
370         }
371         break;
372
373     case UAM_OPTION_SIGNATURE:
374         *buf = (void *)obj->dsi->signature;
375         if (len)
376             *len = 16;
377         break;
378
379     case UAM_OPTION_RANDNUM: /* returns a random number in 4-byte units. */
380         if (!len)
381             return -1;
382
383         return uam_random_string(obj, option, *len);
384         break;
385
386     case UAM_OPTION_HOSTNAME:
387         *buf = obj->options.hostname;
388         if (len)
389             *len = strlen(obj->options.hostname);
390         break;
391
392     case UAM_OPTION_CLIENTNAME:
393     {
394         struct DSI *dsi = obj->dsi;
395         const struct sockaddr *sa;
396         static char hbuf[NI_MAXHOST];
397         
398         sa = (struct sockaddr *)&dsi->client;
399         if (getnameinfo(sa, sizeof(dsi->client), hbuf, sizeof(hbuf), NULL, 0, 0) == 0)
400             *buf = hbuf;
401         else
402             *buf = getip_string((struct sockaddr *)&dsi->client);
403
404         break;
405     }
406     case UAM_OPTION_COOKIE:
407         /* it's up to the uam to actually store something useful here.
408          * this just passes back a handle to the cookie. the uam side
409          * needs to do something like **buf = (void *) cookie to store
410          * the cookie. */
411         *buf = (void *) &obj->uam_cookie;
412         break;
413     case UAM_OPTION_KRB5SERVICE:
414         *buf = obj->options.k5service;
415         if (len)
416             *len = (*buf)?strlen(*buf):0;
417         break;
418     case UAM_OPTION_KRB5REALM:
419         *buf = obj->options.k5realm;
420         if (len)
421             *len = (*buf)?strlen(*buf):0;
422         break;
423     case UAM_OPTION_FQDN:
424         *buf = obj->options.fqdn;
425         if (len)
426             *len = (*buf)?strlen(*buf):0;
427         break;
428     case UAM_OPTION_MACCHARSET:
429         *((int *) option) = obj->options.maccharset;
430         *len = sizeof(obj->options.maccharset);
431         break;
432     case UAM_OPTION_UNIXCHARSET:
433         *((int *) option) = obj->options.unixcharset;
434         *len = sizeof(obj->options.unixcharset);
435         break;
436     case UAM_OPTION_SESSIONINFO:
437         *sinfo = &(obj->sinfo);
438         break;
439     default:
440         return -1;
441         break;
442     }
443
444     return 0;
445 }
446
447 /* if we need to maintain a connection, this is how we do it.
448  * because an action pointer gets passed in, we can stream 
449  * DSI connections */
450 int uam_afp_read(void *handle, char *buf, size_t *buflen,
451                  int (*action)(void *, void *, const int))
452 {
453     AFPObj *obj = handle;
454     int len;
455
456     if (!obj)
457         return AFPERR_PARAM;
458
459         len = dsi_writeinit(obj->dsi, buf, *buflen);
460         if (!len || ((len = action(handle, buf, len)) < 0)) {
461             dsi_writeflush(obj->dsi);
462             goto uam_afp_read_err;
463         }
464
465         while ((len = (dsi_write(obj->dsi, buf, *buflen)))) {
466             if ((len = action(handle, buf, len)) < 0) {
467                 dsi_writeflush(obj->dsi);
468                 goto uam_afp_read_err;
469             }
470         }
471     return 0;
472
473 uam_afp_read_err:
474     *buflen = 0;
475     return len;
476 }
477
478 #ifdef TRU64
479 void uam_afp_getcmdline( int *ac, char ***av )
480 {
481     afp_get_cmdline( ac, av );
482 }
483
484 int uam_sia_validate_user(sia_collect_func_t * collect, int argc, char **argv,
485                          char *hostname, char *username, char *tty,
486                          int colinput, char *gssapi, char *passphrase)
487 /* A clone of the Tru64 system function sia_validate_user() that calls
488  * sia_ses_authent() rather than sia_ses_reauthent()   
489  * Added extra code to take into account suspected SIA bug whereby it clobbers
490  * the signal handler on SIGALRM (tickle) installed by Netatalk/afpd
491  */
492 {
493        SIAENTITY *entity = NULL;
494        struct sigaction act;
495        int rc;
496
497        if ((rc=sia_ses_init(&entity, argc, argv, hostname, username, tty,
498                             colinput, gssapi)) != SIASUCCESS) {
499                LOG(log_error, logtype_afpd, "cannot initialise SIA");
500                return SIAFAIL;
501        }
502
503        /* save old action for restoration later */
504        if (sigaction(SIGALRM, NULL, &act))
505                LOG(log_error, logtype_afpd, "cannot save SIGALRM handler");
506
507        if ((rc=sia_ses_authent(collect, passphrase, entity)) != SIASUCCESS) {
508                /* restore old action after clobbering by sia_ses_authent() */
509                if (sigaction(SIGALRM, &act, NULL))
510                        LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler");
511
512                LOG(log_info, logtype_afpd, "unsuccessful login for %s",
513 (hostname?hostname:"(null)"));
514                return SIAFAIL;
515        }
516        LOG(log_info, logtype_afpd, "successful login for %s",
517 (hostname?hostname:"(null)"));
518
519        /* restore old action after clobbering by sia_ses_authent() */   
520        if (sigaction(SIGALRM, &act, NULL))
521                LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler");
522
523        sia_ses_release(&entity);
524
525        return SIASUCCESS;
526 }
527 #endif /* TRU64 */
528
529 /* --- papd-specific functions (just placeholders) --- */
530 struct papfile;
531
532 UAM_MODULE_EXPORT void append(struct papfile *pf  _U_, const char *data _U_, int len _U_)
533 {
534     return;
535 }