]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/uam.c
Fix build
[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         gecoslen = convert_string(obj->options.unixcharset, CH_UCS2, 
246                                 pwent->pw_gecos, -1, user, sizeof(username));
247         pwnamelen = convert_string(obj->options.unixcharset, CH_UCS2, 
248                                 pwent->pw_name, -1, pwname, sizeof(username));
249         if ((size_t)-1 == gecoslen && (size_t)-1 == pwnamelen)
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     uint32_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->dsi);
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_EXPIRETIME: /* not implemented */
368         default:
369             return -1;
370             break;
371         }
372         break;
373
374     case UAM_OPTION_SIGNATURE:
375         *buf = (void *)obj->dsi->signature;
376         if (len)
377             *len = 16;
378         break;
379
380     case UAM_OPTION_RANDNUM: /* returns a random number in 4-byte units. */
381         if (!len)
382             return -1;
383
384         return uam_random_string(obj, option, *len);
385         break;
386
387     case UAM_OPTION_HOSTNAME:
388         *buf = obj->options.hostname;
389         if (len)
390             *len = strlen(obj->options.hostname);
391         break;
392
393     case UAM_OPTION_CLIENTNAME:
394     {
395         struct DSI *dsi = obj->dsi;
396         const struct sockaddr *sa;
397         static char hbuf[NI_MAXHOST];
398         
399         sa = (struct sockaddr *)&dsi->client;
400         if (getnameinfo(sa, sizeof(dsi->client), hbuf, sizeof(hbuf), NULL, 0, 0) == 0)
401             *buf = hbuf;
402         else
403             *buf = getip_string((struct sockaddr *)&dsi->client);
404
405         break;
406     }
407     case UAM_OPTION_COOKIE:
408         /* it's up to the uam to actually store something useful here.
409          * this just passes back a handle to the cookie. the uam side
410          * needs to do something like **buf = (void *) cookie to store
411          * the cookie. */
412         *buf = (void *) &obj->uam_cookie;
413         break;
414     case UAM_OPTION_KRB5SERVICE:
415         *buf = obj->options.k5service;
416         if (len)
417             *len = (*buf)?strlen(*buf):0;
418         break;
419     case UAM_OPTION_KRB5REALM:
420         *buf = obj->options.k5realm;
421         if (len)
422             *len = (*buf)?strlen(*buf):0;
423         break;
424     case UAM_OPTION_FQDN:
425         *buf = obj->options.fqdn;
426         if (len)
427             *len = (*buf)?strlen(*buf):0;
428         break;
429     case UAM_OPTION_MACCHARSET:
430         *((int *) option) = obj->options.maccharset;
431         *len = sizeof(obj->options.maccharset);
432         break;
433     case UAM_OPTION_UNIXCHARSET:
434         *((int *) option) = obj->options.unixcharset;
435         *len = sizeof(obj->options.unixcharset);
436         break;
437     case UAM_OPTION_SESSIONINFO:
438         *sinfo = &(obj->sinfo);
439         break;
440     default:
441         return -1;
442         break;
443     }
444
445     return 0;
446 }
447
448 /* if we need to maintain a connection, this is how we do it.
449  * because an action pointer gets passed in, we can stream 
450  * DSI connections */
451 int uam_afp_read(void *handle, char *buf, size_t *buflen,
452                  int (*action)(void *, void *, const int))
453 {
454     AFPObj *obj = handle;
455     int len;
456
457     if (!obj)
458         return AFPERR_PARAM;
459
460         len = dsi_writeinit(obj->dsi, buf, *buflen);
461         if (!len || ((len = action(handle, buf, len)) < 0)) {
462             dsi_writeflush(obj->dsi);
463             goto uam_afp_read_err;
464         }
465
466         while ((len = (dsi_write(obj->dsi, buf, *buflen)))) {
467             if ((len = action(handle, buf, len)) < 0) {
468                 dsi_writeflush(obj->dsi);
469                 goto uam_afp_read_err;
470             }
471         }
472     return 0;
473
474 uam_afp_read_err:
475     *buflen = 0;
476     return len;
477 }
478
479 #ifdef TRU64
480 void uam_afp_getcmdline( int *ac, char ***av )
481 {
482     afp_get_cmdline( ac, av );
483 }
484
485 int uam_sia_validate_user(sia_collect_func_t * collect, int argc, char **argv,
486                          char *hostname, char *username, char *tty,
487                          int colinput, char *gssapi, char *passphrase)
488 /* A clone of the Tru64 system function sia_validate_user() that calls
489  * sia_ses_authent() rather than sia_ses_reauthent()   
490  * Added extra code to take into account suspected SIA bug whereby it clobbers
491  * the signal handler on SIGALRM (tickle) installed by Netatalk/afpd
492  */
493 {
494        SIAENTITY *entity = NULL;
495        struct sigaction act;
496        int rc;
497
498        if ((rc=sia_ses_init(&entity, argc, argv, hostname, username, tty,
499                             colinput, gssapi)) != SIASUCCESS) {
500                LOG(log_error, logtype_afpd, "cannot initialise SIA");
501                return SIAFAIL;
502        }
503
504        /* save old action for restoration later */
505        if (sigaction(SIGALRM, NULL, &act))
506                LOG(log_error, logtype_afpd, "cannot save SIGALRM handler");
507
508        if ((rc=sia_ses_authent(collect, passphrase, entity)) != SIASUCCESS) {
509                /* restore old action after clobbering by sia_ses_authent() */
510                if (sigaction(SIGALRM, &act, NULL))
511                        LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler");
512
513                LOG(log_info, logtype_afpd, "unsuccessful login for %s",
514 (hostname?hostname:"(null)"));
515                return SIAFAIL;
516        }
517        LOG(log_info, logtype_afpd, "successful login for %s",
518 (hostname?hostname:"(null)"));
519
520        /* restore old action after clobbering by sia_ses_authent() */   
521        if (sigaction(SIGALRM, &act, NULL))
522                LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler");
523
524        sia_ses_release(&entity);
525
526        return SIASUCCESS;
527 }
528 #endif /* TRU64 */
529
530 /* --- papd-specific functions (just placeholders) --- */
531 struct papfile;
532
533 UAM_MODULE_EXPORT void append(struct papfile *pf  _U_, const char *data _U_, int len _U_)
534 {
535     return;
536 }