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