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