]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/uam.c
Remove TimeMachine volume used size FCE event
[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
35 #include "afp_config.h"
36 #include "auth.h"
37 #include "uam_auth.h"
38
39 #ifdef TRU64
40 #include <netdb.h>
41 #include <sia.h>
42 #include <siad.h>
43 #include <signal.h>
44 #endif /* TRU64 */
45
46 /* --- server uam functions -- */
47
48 /* uam_load. uams must have a uam_setup function. */
49 struct uam_mod *uam_load(const char *path, const char *name)
50 {
51     char buf[MAXPATHLEN + 1], *p;
52     struct uam_mod *mod;
53     void *module;
54
55     if ((module = mod_open(path)) == NULL) {
56         LOG(log_error, logtype_afpd, "uam_load(%s): failed to load: %s", name, mod_error());
57         return NULL;
58     }
59
60     if ((mod = (struct uam_mod *) malloc(sizeof(struct uam_mod))) == NULL) {
61         LOG(log_error, logtype_afpd, "uam_load(%s): malloc failed", name);
62         goto uam_load_fail;
63     }
64
65     strlcpy(buf, name, sizeof(buf));
66     if ((p = strchr(buf, '.')))
67         *p = '\0';
68
69     if ((mod->uam_fcn = mod_symbol(module, buf)) == NULL) {
70         LOG(log_error, logtype_afpd, "uam_load(%s): mod_symbol error for symbol %s",
71             name,
72             buf);
73         goto uam_load_err;
74     }
75
76     if (mod->uam_fcn->uam_type != UAM_MODULE_SERVER) {
77         LOG(log_error, logtype_afpd, "uam_load(%s): attempted to load a non-server module",
78             name);
79         goto uam_load_err;
80     }
81
82     /* version check would go here */
83
84     if (!mod->uam_fcn->uam_setup ||
85             ((*mod->uam_fcn->uam_setup)(name) < 0)) {
86         LOG(log_error, logtype_afpd, "uam_load(%s): uam_setup failed", name);
87         goto uam_load_err;
88     }
89
90     mod->uam_module = module;
91     return mod;
92
93 uam_load_err:
94     free(mod);
95 uam_load_fail:
96     mod_close(module);
97     return NULL;
98 }
99
100 /* unload the module. we check for a cleanup function, but we don't
101  * die if one doesn't exist. however, things are likely to leak without one.
102  */
103 void uam_unload(struct uam_mod *mod)
104 {
105     if (mod->uam_fcn->uam_cleanup)
106         (*mod->uam_fcn->uam_cleanup)();
107
108     mod_close(mod->uam_module);
109     free(mod);
110 }
111
112 /* -- client-side uam functions -- */
113 /* set up stuff for this uam. */
114 int uam_register(const int type, const char *path, const char *name, ...)
115 {
116     va_list ap;
117     struct uam_obj *uam;
118     int ret;
119
120     if (!name)
121         return -1;
122
123     /* see if it already exists. */
124     if ((uam = auth_uamfind(type, name, strlen(name)))) {
125         if (strcmp(uam->uam_path, path)) {
126             /* it exists, but it's not the same module. */
127             LOG(log_error, logtype_afpd, "uam_register: \"%s\" already loaded by %s",
128                 name, path);
129             return -1;
130         }
131         uam->uam_count++;
132         return 0;
133     }
134
135     /* allocate space for uam */
136     if ((uam = calloc(1, sizeof(struct uam_obj))) == NULL)
137         return -1;
138
139     uam->uam_name = name;
140     uam->uam_path = strdup(path);
141     uam->uam_count++;
142
143     va_start(ap, name);
144     switch (type) {
145     case UAM_SERVER_LOGIN_EXT: /* expect four arguments */
146         uam->u.uam_login.login = va_arg(ap, void *);
147         uam->u.uam_login.logincont = va_arg(ap, void *);
148         uam->u.uam_login.logout = va_arg(ap, void *);
149         uam->u.uam_login.login_ext = va_arg(ap, void *);
150         break;
151     
152     case UAM_SERVER_LOGIN: /* expect three arguments */
153         uam->u.uam_login.login_ext = NULL;
154         uam->u.uam_login.login = va_arg(ap, void *);
155         uam->u.uam_login.logincont = va_arg(ap, void *);
156         uam->u.uam_login.logout = va_arg(ap, void *);
157         break;
158     case UAM_SERVER_CHANGEPW: /* one argument */
159         uam->u.uam_changepw = va_arg(ap, void *);
160         break;
161     case UAM_SERVER_PRINTAUTH: /* x arguments */
162     default:
163         break;
164     }
165     va_end(ap);
166
167     /* attach to other uams */
168     ret = auth_register(type, uam);
169     if ( ret) {
170         free(uam->uam_path);
171         free(uam);
172     }
173
174     return ret;
175 }
176
177 void uam_unregister(const int type, const char *name)
178 {
179     struct uam_obj *uam;
180
181     if (!name)
182         return;
183
184     uam = auth_uamfind(type, name, strlen(name));
185     if (!uam || --uam->uam_count > 0)
186         return;
187
188     auth_unregister(uam);
189     free(uam->uam_path);
190     free(uam);
191 }
192
193 /* --- helper functions for plugin uams --- 
194  * name: user name
195  * len:  size of name buffer.
196 */
197
198 struct passwd *uam_getname(void *private, char *name, const int len)
199 {
200     AFPObj *obj = private;
201     struct passwd *pwent;
202     static char username[256];
203     static char user[256];
204     static char pwname[256];
205     char *p;
206     size_t namelen, gecoslen = 0, pwnamelen = 0;
207
208     if ((pwent = getpwnam(name)))
209         return pwent;
210         
211     /* if we have a NT domain name try with it */
212     if (obj->options.ntdomain && obj->options.ntseparator) {
213         /* FIXME What about charset ? */
214         size_t ulen = strlen(obj->options.ntdomain) + strlen(obj->options.ntseparator) + strlen(name);
215         if ((p = malloc(ulen +1))) {
216             strcpy(p, obj->options.ntdomain);
217             strcat(p, obj->options.ntseparator);
218             strcat(p, name);
219             pwent = getpwnam(p);
220             free(p);
221             if (pwent) {
222                 int len = strlen(pwent->pw_name);              
223                 if (len < MAXUSERLEN) {
224                     strncpy(name,pwent->pw_name, MAXUSERLEN);  
225                 }else{
226                     LOG(log_error, logtype_uams, "MAJOR:The name %s is longer than %d",pwent->pw_name,MAXUSERLEN);
227                 }
228
229                 return pwent;
230             }
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 }