1/*
2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved.  See COPYRIGHT.
4 */
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif /* HAVE_CONFIG_H */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <errno.h>
13#ifdef HAVE_UNISTD_H
14#include <unistd.h>
15#endif /* HAVE_UNISTD_H */
16#include <sys/types.h>
17#include <sys/param.h>
18#include <sys/stat.h>
19#include <netatalk/endian.h>
20#include <atalk/afp.h>
21#include <atalk/compat.h>
22#include <atalk/util.h>
23#include <limits.h>
24#include <string.h>
25#include <ctype.h>
26#include <time.h>
27#include <pwd.h>
28#include <grp.h>
29
30#ifdef TRU64
31#include <netdb.h>
32#include <arpa/inet.h>
33#include <sia.h>
34#include <siad.h>
35
36extern void afp_get_cmdline( int *ac, char ***av );
37#endif /* TRU64 */
38
39#include <atalk/logger.h>
40#include <atalk/server_ipc.h>
41#include <atalk/uuid.h>
42#include <atalk/globals.h>
43
44#include "auth.h"
45#include "uam_auth.h"
46#include "switch.h"
47#include "status.h"
48#include "fork.h"
49#include "extattrs.h"
50#ifdef HAVE_ACLS
51#include "acls.h"
52#endif
53
54int afp_version = 11;
55static int afp_version_index;
56
57uid_t   uuid;
58
59#if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
60
61int *groups;
62#define GROUPS_SIZE sizeof(int)
63
64#else /* sun __svr4__ ultrix */
65
66gid_t   *groups;
67#define GROUPS_SIZE sizeof(gid_t)
68#endif /* sun ultrix */
69
70int ngroups;
71
72static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
73static struct uam_obj uam_login = {"", "", 0, {{NULL, NULL, NULL, NULL }}, &uam_login,
74                                   &uam_login};
75static struct uam_obj uam_changepw = {"", "", 0, {{NULL, NULL, NULL, NULL}}, &uam_changepw,
76                                      &uam_changepw};
77
78static struct uam_obj *afp_uam = NULL;
79
80
81void status_versions(char *data,
82#ifndef NO_DDP
83                     const ASP asp,
84#endif
85                     const DSI *dsi)
86{
87    char                *start = data;
88    u_int16_t           status;
89    int         len, num, i, count = 0;
90
91    memcpy(&status, start + AFPSTATUS_VERSOFF, sizeof(status));
92    num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ] );
93
94    for ( i = 0; i < num; i++ ) {
95#ifndef NO_DDP
96        if ( !asp && (afp_versions[ i ].av_number <= 21)) continue;
97#endif /* ! NO_DDP */
98        if ( !dsi && (afp_versions[ i ].av_number >= 22)) continue;
99        count++;
100    }
101    data += ntohs( status );
102    *data++ = count;
103
104    for ( i = 0; i < num; i++ ) {
105#ifndef NO_DDP
106        if ( !asp && (afp_versions[ i ].av_number <= 21)) continue;
107#endif /* ! NO_DDP */
108        if ( !dsi && (afp_versions[ i ].av_number >= 22)) continue;
109        len = strlen( afp_versions[ i ].av_name );
110        *data++ = len;
111        memcpy( data, afp_versions[ i ].av_name , len );
112        data += len;
113    }
114    status = htons( data - start );
115    memcpy(start + AFPSTATUS_UAMSOFF, &status, sizeof(status));
116}
117
118void status_uams(char *data, const char *authlist)
119{
120    char                *start = data;
121    u_int16_t           status;
122    struct uam_obj      *uams;
123    int         len, num = 0;
124
125    memcpy(&status, start + AFPSTATUS_UAMSOFF, sizeof(status));
126    uams = &uam_login;
127    while ((uams = uams->uam_prev) != &uam_login) {
128        if (strstr(authlist, uams->uam_path))
129            num++;
130    }
131
132    data += ntohs( status );
133    *data++ = num;
134    while ((uams = uams->uam_prev) != &uam_login) {
135        if (strstr(authlist, uams->uam_path)) {
136            LOG(log_info, logtype_afpd, "uam: \"%s\" available", uams->uam_name);
137            len = strlen( uams->uam_name);
138            *data++ = len;
139            memcpy( data, uams->uam_name, len );
140            data += len;
141        }
142    }
143
144    /* icon offset */
145    status = htons(data - start);
146    memcpy(start + AFPSTATUS_ICONOFF, &status, sizeof(status));
147}
148
149/* handle errors by closing the connection. this is only needed
150 * by the afp_* functions. */
151static int send_reply(const AFPObj *obj, const int err)
152{
153    if ((err == AFP_OK) || (err == AFPERR_AUTHCONT))
154        return err;
155
156    obj->reply(obj->handle, err);
157    obj->exit(0);
158
159    return AFP_OK;
160}
161
162static int afp_errpwdexpired(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_,
163                             char *rbuf _U_, size_t *rbuflen)
164{
165    *rbuflen = 0;
166    return AFPERR_PWDEXPR;
167}
168
169static int afp_null_nolog(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_,
170                          char *rbuf _U_, size_t *rbuflen)
171{
172    *rbuflen = 0;
173    return( AFPERR_NOOP );
174}
175
176static int set_auth_switch(int expired)
177{
178    int i;
179
180    if (expired) {
181        /*
182         * BF: expired password handling
183         * to allow the user to change his/her password we have to allow login
184         * but every following call except for FPChangePassword will be thrown
185         * away with an AFPERR_PWDEXPR error. (thanks to Leland Wallace from Apple
186         * for clarifying this)
187         */
188
189        for (i=0; i<=0xff; i++) {
190            uam_afpserver_action(i, UAM_AFPSERVER_PREAUTH, afp_errpwdexpired, NULL);
191        }
192        uam_afpserver_action(AFP_LOGOUT, UAM_AFPSERVER_PREAUTH, afp_logout, NULL);
193        uam_afpserver_action(AFP_CHANGEPW, UAM_AFPSERVER_PREAUTH, afp_changepw, NULL);
194    }
195    else {
196        afp_switch = postauth_switch;
197        switch (afp_version) {
198
199        case 33:
200        case 32:
201#ifdef HAVE_ACLS
202            uam_afpserver_action(AFP_GETACL, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
203            uam_afpserver_action(AFP_SETACL, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
204            uam_afpserver_action(AFP_ACCESS, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
205#endif /* HAVE_ACLS */
206            uam_afpserver_action(AFP_GETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
207            uam_afpserver_action(AFP_SETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
208            uam_afpserver_action(AFP_REMOVEATTR, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
209            uam_afpserver_action(AFP_LISTEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_listextattr, NULL);
210
211        case 31:
212            uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
213            uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
214            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
215            uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
216
217        case 30:
218            uam_afpserver_action(AFP_ENUMERATE_EXT, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext, NULL);
219            uam_afpserver_action(AFP_BYTELOCK_EXT,  UAM_AFPSERVER_POSTAUTH, afp_bytelock_ext, NULL);
220            /* catsearch_ext uses the same packet as catsearch FIXME double check this, it wasn't true for enue
221               enumerate_ext */
222            uam_afpserver_action(AFP_CATSEARCH_EXT, UAM_AFPSERVER_POSTAUTH, afp_catsearch_ext, NULL);
223            uam_afpserver_action(AFP_GETSESSTOKEN,  UAM_AFPSERVER_POSTAUTH, afp_getsession, NULL);
224            uam_afpserver_action(AFP_READ_EXT,      UAM_AFPSERVER_POSTAUTH, afp_read_ext, NULL);
225            uam_afpserver_action(AFP_WRITE_EXT,     UAM_AFPSERVER_POSTAUTH, afp_write_ext, NULL);
226            uam_afpserver_action(AFP_DISCTOLDSESS,  UAM_AFPSERVER_POSTAUTH, afp_disconnect, NULL);
227
228        case 22:
229            /*
230             * If first connection to a server is done in classic AFP2.2 version is used
231             * but OSX uses AFP3.x FPzzz command !
232             */
233            uam_afpserver_action(AFP_ZZZ,  UAM_AFPSERVER_POSTAUTH, afp_zzz, NULL);
234            break;
235        }
236    }
237
238    return AFP_OK;
239}
240
241static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expired)
242{
243#ifdef ADMIN_GRP
244    int admin = 0;
245#endif /* ADMIN_GRP */
246
247#if 0
248    if ( pwd->pw_uid == 0 ) {   /* don't allow root login */
249        LOG(log_error, logtype_afpd, "login: root login denied!" );
250        return AFPERR_NOTAUTH;
251    }
252#endif
253
254    LOG(log_note, logtype_afpd, "%s Login by %s",
255        afp_versions[afp_version_index].av_name, pwd->pw_name);
256
257#ifndef NO_DDP
258    if (obj->proto == AFPPROTO_ASP) {
259        ASP asp = obj->handle;
260        int addr_net = ntohs( asp->asp_sat.sat_addr.s_net );
261        int addr_node  = asp->asp_sat.sat_addr.s_node;
262
263        if (obj->options.authprintdir) {
264            if(addr_net && addr_node) { /* Do we have a valid Appletalk address? */
265                char nodename[256];
266                FILE *fp;
267                int mypid = getpid();
268                struct stat stat_buf;
269
270                sprintf(nodename, "%s/net%d.%dnode%d", obj->options.authprintdir,
271                        addr_net / 256, addr_net % 256, addr_node);
272                LOG(log_info, logtype_afpd, "registering %s (uid %d) on %u.%u as %s",
273                    pwd->pw_name, pwd->pw_uid, addr_net, addr_node, nodename);
274
275                if (stat(nodename, &stat_buf) == 0) { /* file exists */
276                    if (S_ISREG(stat_buf.st_mode)) { /* normal file */
277                        unlink(nodename);
278                        fp = fopen(nodename, "w");
279                        fprintf(fp, "%s:%d\n", pwd->pw_name, mypid);
280                        fclose(fp);
281                        chown( nodename, pwd->pw_uid, -1 );
282                    } else { /* somebody is messing with us */
283                        LOG(log_error, logtype_afpd, "print authfile %s is not a normal file, it will not be modified", nodename );
284                    }
285                } else { /* file 'nodename' does not exist */
286                    fp = fopen(nodename, "w");
287                    fprintf(fp, "%s:%d\n", pwd->pw_name, mypid);
288                    fclose(fp);
289                    chown( nodename, pwd->pw_uid, -1 );
290                }
291            } /* if (addr_net && addr_node ) */
292        } /* if (options->authprintdir) */
293    } /* if (obj->proto == AFPPROTO_ASP) */
294#endif
295
296    if (set_groups(obj, pwd) != 0)
297        return AFPERR_BADUAM;
298
299#ifdef ADMIN_GRP
300    LOG(log_debug, logtype_afpd, "obj->options.admingid == %d", obj->options.admingid);
301
302    if (obj->options.admingid != 0) {
303        int i;
304        for (i = 0; i < ngroups; i++) {
305            if (groups[i] == obj->options.admingid) admin = 1;
306        }
307    }
308    if (admin) {
309        ad_setfuid(0);
310        LOG(log_info, logtype_afpd, "admin login -- %s", pwd->pw_name );
311    }
312    if (!admin)
313#endif /* ADMIN_GRP */
314#ifdef TRU64
315    {
316        struct DSI *dsi = obj->handle;
317        struct hostent *hp;
318        char *clientname;
319        int argc;
320        char **argv;
321        char hostname[256];
322
323        afp_get_cmdline( &argc, &argv );
324
325        hp = gethostbyaddr( (char *) &dsi->client.sin_addr,
326                            sizeof( struct in_addr ),
327                            dsi->client.sin_family );
328
329        if( hp )
330            clientname = hp->h_name;
331        else
332            clientname = inet_ntoa( dsi->client.sin_addr );
333
334        sprintf( hostname, "%s@%s", pwd->pw_name, clientname );
335
336        if( sia_become_user( NULL, argc, argv, hostname, pwd->pw_name,
337                             NULL, FALSE, NULL, NULL,
338                             SIA_BEU_REALLOGIN ) != SIASUCCESS )
339            return AFPERR_BADUAM;
340
341        LOG(log_info, logtype_afpd, "session from %s (%s)", hostname,
342            inet_ntoa( dsi->client.sin_addr ) );
343
344        if (setegid( pwd->pw_gid ) < 0 || seteuid( pwd->pw_uid ) < 0) {
345            LOG(log_error, logtype_afpd, "login: %s %s", pwd->pw_name, strerror(errno) );
346            return AFPERR_BADUAM;
347        }
348    }
349#else /* TRU64 */
350    if (setegid( pwd->pw_gid ) < 0 || seteuid( pwd->pw_uid ) < 0) {
351        LOG(log_error, logtype_afpd, "login: %s %s", pwd->pw_name, strerror(errno) );
352        return AFPERR_BADUAM;
353    }
354#endif /* TRU64 */
355
356    LOG(log_debug, logtype_afpd, "login: supplementary groups: %s", print_groups(ngroups, groups));
357
358    /* There's probably a better way to do this, but for now, we just play root */
359#ifdef ADMIN_GRP
360    if (admin)
361        uuid = 0;
362    else
363#endif /* ADMIN_GRP */
364        uuid = pwd->pw_uid;
365
366    set_auth_switch(expired);
367    /* save our euid, we need it for preexec_close */
368    obj->uid = geteuid();
369    obj->logout = logout;
370
371#ifdef FORCE_UIDGID
372    obj->force_uid = 1;
373    save_uidgid ( &obj->uidgid );
374#endif
375
376    /* pam_umask or similar might have changed our umask */
377    (void)umask(obj->options.umask);
378
379    /* Some PAM module might have reset our signal handlers and timer, so we need to reestablish them */
380    afp_over_dsi_sighandlers(obj);
381
382    return( AFP_OK );
383}
384
385#define GROUPSTR_BUFSIZE 1024
386const char *print_groups(int ngroups, gid_t *groups)
387{
388    static char groupsstr[GROUPSTR_BUFSIZE];
389    int i;
390    char *s = groupsstr;
391
392    if (ngroups == 0)
393        return "-";
394
395    for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
396        s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);
397    }
398
399    return groupsstr;
400}
401
402int set_groups(AFPObj *obj, struct passwd *pwd)
403{
404    if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
405        LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno));
406
407    if ((ngroups = getgroups(0, NULL)) < 0) {
408        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
409        return -1;
410    }
411
412    if (groups)
413        free(groups);
414    if (NULL == (groups = calloc(ngroups, GROUPS_SIZE)) ) {
415        LOG(log_error, logtype_afpd, "login: %s calloc: %d", ngroups);
416        return -1;
417    }
418
419    if ((ngroups = getgroups(ngroups, groups)) < 0 ) {
420        LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
421        return -1;
422    }
423
424    return 0;
425}
426
427/* ---------------------- */
428int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
429{
430    uint32_t data;
431    DSI *dsi = (DSI *)AFPobj->handle;
432
433    *rbuflen = 0;
434    ibuf += 2;
435    ibuflen -= 2;
436
437    if (ibuflen < 4)
438        return AFPERR_MISC;
439    memcpy(&data, ibuf, 4); /* flag */
440    data = ntohl(data);
441
442    /*
443     * Possible sleeping states:
444     * 1) normal sleep: DSI_SLEEPING (up to 10.3)
445     * 2) extended sleep: DSI_SLEEPING | DSI_EXTSLEEP (starting with 10.4)
446     */
447
448    if (data & AFPZZZ_EXT_WAKEUP) {
449        /* wakeup request from exetended sleep */
450        if (dsi->flags & DSI_EXTSLEEP) {
451            LOG(log_note, logtype_afpd, "afp_zzz: waking up from extended sleep");
452            dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP);
453        }
454    } else {
455        /* sleep request */
456        dsi->flags |= DSI_SLEEPING;
457        if (data & AFPZZZ_EXT_SLEEP) {
458            LOG(log_note, logtype_afpd, "afp_zzz: entering extended sleep");
459            dsi->flags |= DSI_EXTSLEEP;
460        } else {
461            LOG(log_note, logtype_afpd, "afp_zzz: entering normal sleep");
462        }
463    }
464
465    /*
466     * According to AFP 3.3 spec we should not return anything,
467     * but eg 10.5.8 server still returns the numbers of hours
468     * the server is keeping the sessino (ie max sleeptime).
469     */
470    data = obj->options.sleep / 120; /* hours */
471    if (!data) {
472        data = 1;
473    }
474    *rbuflen = sizeof(data);
475    data = htonl(data);
476    memcpy(rbuf, &data, sizeof(data));
477    rbuf += sizeof(data);
478
479    return AFP_OK;
480}
481
482/* ---------------------- */
483static int create_session_token(AFPObj *obj)
484{
485    pid_t pid;
486
487    /* use 8 bytes for token as OSX, don't know if it helps */
488    if ( sizeof(pid_t) > SESSIONTOKEN_LEN) {
489        LOG(log_error, logtype_afpd, "sizeof(pid_t) > %u", SESSIONTOKEN_LEN );
490        return AFPERR_MISC;
491    }
492
493    if ( NULL == (obj->sinfo.sessiontoken = malloc(SESSIONTOKEN_LEN)) )
494        return AFPERR_MISC;
495
496    memset(obj->sinfo.sessiontoken, 0, SESSIONTOKEN_LEN);
497    obj->sinfo.sessiontoken_len = SESSIONTOKEN_LEN;
498    pid = getpid();
499    memcpy(obj->sinfo.sessiontoken, &pid, sizeof(pid_t));
500
501    return 0;
502}
503
504static int create_session_key(AFPObj *obj)
505{
506    /* create session key */
507    if (obj->sinfo.sessionkey == NULL) {
508        if (NULL == (obj->sinfo.sessionkey = malloc(SESSIONKEY_LEN)) )
509            return AFPERR_MISC;
510        uam_random_string(obj, obj->sinfo.sessionkey, SESSIONKEY_LEN);
511        obj->sinfo.sessionkey_len = SESSIONKEY_LEN;
512    }
513    return AFP_OK;
514}
515
516
517/* ---------------------- */
518int afp_getsession(
519    AFPObj *obj,
520    char   *ibuf, size_t ibuflen,
521    char   *rbuf, size_t *rbuflen)
522{
523    u_int16_t           type;
524    u_int32_t           idlen = 0;
525    u_int32_t       boottime;
526    u_int32_t           tklen, tp;
527    char                *token;
528    char                *p;
529
530    *rbuflen = 0;
531    tklen = 0;
532
533    if (ibuflen < 2 + sizeof(type)) {
534        return AFPERR_PARAM;
535    }
536
537    ibuf += 2;
538    ibuflen -= 2;
539
540    memcpy(&type, ibuf, sizeof(type));
541    type = ntohs(type);
542    ibuf += sizeof(type);
543    ibuflen -= sizeof(type);
544
545    if ( obj->sinfo.sessiontoken == NULL ) {
546        if ( create_session_token( obj ) )
547            return AFPERR_MISC;
548    }
549
550    /*
551     *
552     */
553    switch (type) {
554    case 0: /* old version ?*/
555        tklen = obj->sinfo.sessiontoken_len;
556        token = obj->sinfo.sessiontoken;
557        break;
558    case 1: /* disconnect */
559    case 2: /* reconnect update id */
560        if (ibuflen >= sizeof(idlen)) {
561            memcpy(&idlen, ibuf, sizeof(idlen));
562            idlen = ntohl(idlen);
563            ibuf += sizeof(idlen);
564            ibuflen -= sizeof(idlen);
565            if (ibuflen < idlen) {
566                return AFPERR_PARAM;
567            }
568            /* memcpy (id, ibuf, idlen) */
569            tklen = obj->sinfo.sessiontoken_len;
570            token = obj->sinfo.sessiontoken;
571        }
572        break;
573    case 3:
574    case 4:
575        if (ibuflen >= 8 ) {
576            p = ibuf;
577            memcpy( &idlen, ibuf, sizeof(idlen));
578            idlen = ntohl(idlen);
579            ibuf += sizeof(idlen);
580            ibuflen -= sizeof(idlen);
581            ibuf += sizeof(boottime);
582            ibuflen -= sizeof(boottime);
583            if (ibuflen < idlen || idlen > (90-10)) {
584                return AFPERR_PARAM;
585            }
586            if (!obj->sinfo.clientid) {
587                obj->sinfo.clientid = malloc(idlen + 8);
588                memcpy(obj->sinfo.clientid, p, idlen + 8);
589                obj->sinfo.clientid_len = idlen + 8;
590            }
591            if (ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p) != 0)
592                return AFPERR_MISC;
593            tklen = obj->sinfo.sessiontoken_len;
594            token = obj->sinfo.sessiontoken;
595        }
596        break;
597    case 8: /* Panther Kerberos Token */
598        tklen = obj->sinfo.cryptedkey_len;
599        token = obj->sinfo.cryptedkey;
600        break;
601    default:
602        return AFPERR_NOOP;
603        break;
604
605    }
606
607    if (tklen == 0)
608        return AFPERR_MISC;
609
610    tp = htonl(tklen);
611    memcpy(rbuf, &tp, sizeof(tklen));
612    rbuf += sizeof(tklen);
613    *rbuflen += sizeof(tklen);
614
615    memcpy(rbuf, token, tklen);
616    *rbuflen += tklen;
617
618    return AFP_OK;
619}
620
621/* ---------------------- */
622int afp_disconnect(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
623{
624    DSI                 *dsi = (DSI *)obj->handle;
625    u_int16_t           type;
626    u_int32_t           tklen;
627    pid_t               token;
628    int                 i;
629
630    *rbuflen = 0;
631    ibuf += 2;
632
633#if 0
634    /* check for guest user */
635    if ( 0 == (strcasecmp(obj->username, obj->options.guest)) ) {
636        return AFPERR_MISC;
637    }
638#endif
639
640    memcpy(&type, ibuf, sizeof(type));
641    type = ntohs(type);
642    ibuf += sizeof(type);
643
644    memcpy(&tklen, ibuf, sizeof(tklen));
645    tklen = ntohl(tklen);
646    ibuf += sizeof(tklen);
647
648    if ( sizeof(pid_t) > SESSIONTOKEN_LEN) {
649        LOG(log_error, logtype_afpd, "sizeof(pid_t) > %u", SESSIONTOKEN_LEN );
650        return AFPERR_MISC;
651    }
652    if (tklen != SESSIONTOKEN_LEN) {
653        return AFPERR_MISC;
654    }
655    tklen = sizeof(pid_t);
656    memcpy(&token, ibuf, tklen);
657
658    /* our stuff is pid + zero pad */
659    ibuf += tklen;
660    for (i = tklen; i < SESSIONTOKEN_LEN; i++, ibuf++) {
661        if (*ibuf != 0) {
662            return AFPERR_MISC;
663        }
664    }
665
666    LOG(log_note, logtype_afpd, "afp_disconnect: trying primary reconnect");
667    dsi->flags |= DSI_RECONINPROG;
668
669    /* Deactivate tickle timer */
670    const struct itimerval none = {{0, 0}, {0, 0}};
671    setitimer(ITIMER_REAL, &none, NULL);
672
673    /* check for old session, possibly transfering session from here to there */
674    if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) != 0)
675        goto exit;
676    /* write uint16_t DSI request ID */
677    if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
678        LOG(log_error, logtype_afpd, "afp_disconnect: couldn't send DSI request ID");
679        goto exit;
680    }
681    /* now send our connected AFP client socket */
682    if (send_fd(obj->ipc_fd, dsi->socket) != 0)
683        goto exit;
684    /* Now see what happens: either afpd master sends us SIGTERM because our session */
685    /* has been transfered to a old disconnected session, or we continue    */
686    sleep(5);
687
688    if (!(dsi->flags & DSI_RECONINPROG)) { /* deleted in SIGTERM handler */
689        /* Reconnect succeeded, we exit now after sleeping some more */
690        sleep(2); /* sleep some more to give the recon. session time */
691        LOG(log_note, logtype_afpd, "afp_disconnect: primary reconnect succeeded");
692        exit(0);
693    }
694
695exit:
696    /* Reinstall tickle timer */
697    setitimer(ITIMER_REAL, &dsi->timer, NULL);
698
699    LOG(log_error, logtype_afpd, "afp_disconnect: primary reconnect failed");
700    return AFPERR_MISC;
701}
702
703/* ---------------------- */
704static int get_version(AFPObj *obj, char *ibuf, size_t ibuflen, size_t len)
705{
706    int num,i;
707
708    if (!len || len > ibuflen)
709        return AFPERR_BADVERS;
710
711    num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ]);
712    for ( i = 0; i < num; i++ ) {
713        if ( strncmp( ibuf, afp_versions[ i ].av_name , len ) == 0 ) {
714            afp_version = afp_versions[ i ].av_number;
715            afp_version_index = i;
716            break;
717        }
718    }
719    if ( i == num )                 /* An inappropo version */
720        return AFPERR_BADVERS ;
721
722    if (afp_version >= 30 && obj->proto != AFPPROTO_DSI)
723        return AFPERR_BADVERS ;
724
725    /* FIXME Hack */
726    if (afp_version >= 30 && sizeof(off_t) != 8) {
727        LOG(log_error, logtype_afpd, "get_version: no LARGE_FILE support recompile!" );
728        return AFPERR_BADVERS ;
729    }
730
731    return 0;
732}
733
734/* ---------------------- */
735int afp_login(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
736{
737    struct passwd *pwd = NULL;
738    size_t len;
739    int     i;
740
741    *rbuflen = 0;
742
743    if ( nologin & 1)
744        return send_reply(obj, AFPERR_SHUTDOWN );
745
746    if (ibuflen < 2)
747        return send_reply(obj, AFPERR_BADVERS );
748
749    ibuf++;
750    len = (unsigned char) *ibuf++;
751    ibuflen -= 2;
752
753    i = get_version(obj, ibuf, ibuflen, len);
754    if (i)
755        return send_reply(obj, i );
756
757    if (ibuflen <= len)
758        return send_reply(obj, AFPERR_BADUAM);
759
760    ibuf += len;
761    ibuflen -= len;
762
763    len = (unsigned char) *ibuf++;
764    ibuflen--;
765
766    if (!len || len > ibuflen)
767        return send_reply(obj, AFPERR_BADUAM);
768
769    if (NULL == (afp_uam = auth_uamfind(UAM_SERVER_LOGIN, ibuf, len)) )
770        return send_reply(obj, AFPERR_BADUAM);
771    ibuf += len;
772    ibuflen -= len;
773
774    if (AFP_OK != (i = create_session_key(obj)) )
775        return send_reply(obj, i);
776
777    i = afp_uam->u.uam_login.login(obj, &pwd, ibuf, ibuflen, rbuf, rbuflen);
778
779    if (!pwd || ( i != AFP_OK && i != AFPERR_PWDEXPR))
780        return send_reply(obj, i);
781
782    return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout, ((i==AFPERR_PWDEXPR)?1:0)));
783}
784
785/* ---------------------- */
786int afp_login_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
787{
788    struct passwd *pwd = NULL;
789    size_t  len;
790    int     i;
791    char        type;
792    u_int16_t   len16;
793    char        *username;
794
795    *rbuflen = 0;
796
797    if ( nologin & 1)
798        return send_reply(obj, AFPERR_SHUTDOWN );
799
800    if (ibuflen < 5)
801        return send_reply(obj, AFPERR_BADVERS );
802
803    ibuf++;
804    ibuf++;     /* pad  */
805    ibuf +=2;   /* flag */
806
807    len = (unsigned char) *ibuf;
808    ibuf++;
809    ibuflen -= 5;
810
811    i = get_version(obj, ibuf, ibuflen, len);
812    if (i)
813        return send_reply(obj, i );
814
815    if (ibuflen <= len)
816        return send_reply(obj, AFPERR_BADUAM);
817
818    ibuf    += len;
819    ibuflen -= len;
820
821    len = (unsigned char) *ibuf;
822    ibuf++;
823    ibuflen--;
824
825    if (!len || len > ibuflen)
826        return send_reply(obj, AFPERR_BADUAM);
827
828    if ((afp_uam = auth_uamfind(UAM_SERVER_LOGIN, ibuf, len)) == NULL)
829        return send_reply(obj, AFPERR_BADUAM);
830    ibuf    += len;
831    ibuflen -= len;
832
833    if (!afp_uam->u.uam_login.login_ext) {
834        LOG(log_error, logtype_afpd, "login_ext: uam %s not AFP 3 ready!", afp_uam->uam_name );
835        return send_reply(obj, AFPERR_BADUAM);
836    }
837    /* user name */
838    if (ibuflen <= 1 +sizeof(len16))
839        return send_reply(obj, AFPERR_PARAM);
840    type = *ibuf;
841    username = ibuf;
842    ibuf++;
843    ibuflen--;
844    if (type != 3)
845        return send_reply(obj, AFPERR_PARAM);
846
847    memcpy(&len16, ibuf, sizeof(len16));
848    ibuf += sizeof(len16);
849    ibuflen -= sizeof(len16);
850    len = ntohs(len16);
851    if (len > ibuflen)
852        return send_reply(obj, AFPERR_PARAM);
853    ibuf += len;
854    ibuflen -= len;
855
856    /* directory service name */
857    if (!ibuflen)
858        return send_reply(obj, AFPERR_PARAM);
859    type = *ibuf;
860    ibuf++;
861    ibuflen--;
862
863    switch(type) {
864    case 1:
865    case 2:
866        if (!ibuflen)
867            return send_reply(obj, AFPERR_PARAM);
868        len = (unsigned char) *ibuf;
869        ibuf++;
870        ibuflen--;
871        break;
872    case 3:
873        /* With "No User Authen" it is equal */
874        if (ibuflen < sizeof(len16))
875            return send_reply(obj, AFPERR_PARAM);
876        memcpy(&len16, ibuf, sizeof(len16));
877        ibuf += sizeof(len16);
878        ibuflen -= sizeof(len16);
879        len = ntohs(len16);
880        break;
881    default:
882        return send_reply(obj, AFPERR_PARAM);
883    }
884#if 0
885    if (len != 0) {
886        LOG(log_error, logtype_afpd, "login_ext: directory service path not null!" );
887        return send_reply(obj, AFPERR_PARAM);
888    }
889#endif
890    ibuf += len;
891    ibuflen -= len;
892
893    /* Pad */
894    if (ibuflen && ((unsigned long) ibuf & 1)) { /* pad character */
895        ibuf++;
896        ibuflen--;
897    }
898
899    if (AFP_OK != (i = create_session_key(obj)) ) {
900        return send_reply(obj, i);
901    }
902
903    /* FIXME user name are in UTF8 */
904    i = afp_uam->u.uam_login.login_ext(obj, username, &pwd, ibuf, ibuflen, rbuf, rbuflen);
905
906    if (!pwd || ( i != AFP_OK && i != AFPERR_PWDEXPR))
907        return send_reply(obj, i);
908
909    return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout, ((i==AFPERR_PWDEXPR)?1:0)));
910}
911
912/* ---------------------- */
913int afp_logincont(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
914{
915    struct passwd *pwd = NULL;
916    int err;
917
918    if ( afp_uam == NULL || afp_uam->u.uam_login.logincont == NULL || ibuflen < 2 ) {
919        *rbuflen = 0;
920        return send_reply(obj, AFPERR_NOTAUTH );
921    }
922
923    ibuf += 2; ibuflen -= 2;
924    err = afp_uam->u.uam_login.logincont(obj, &pwd, ibuf, ibuflen,
925                                         rbuf, rbuflen);
926    if (!pwd || ( err != AFP_OK && err != AFPERR_PWDEXPR))
927        return send_reply(obj, err);
928
929    return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout, ((err==AFPERR_PWDEXPR)?1:0)));
930}
931
932
933int afp_logout(AFPObj *obj, char *ibuf _U_, size_t ibuflen  _U_, char *rbuf  _U_, size_t *rbuflen)
934{
935    DSI *dsi = (DSI *)(obj->handle);
936
937    LOG(log_note, logtype_afpd, "AFP logout by %s", obj->username);
938    of_close_all_forks();
939    close_all_vol();
940    dsi->flags = DSI_AFP_LOGGED_OUT;
941    *rbuflen = 0;
942    return AFP_OK;
943}
944
945
946
947/* change password  --
948 * NOTE: an FPLogin must already have completed successfully for this
949 *       to work. this also does a little pre-processing before it hands
950 *       it off to the uam.
951 */
952int afp_changepw(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
953{
954    char username[MACFILELEN + 1], *start = ibuf;
955    struct uam_obj *uam;
956    struct passwd *pwd;
957    size_t len;
958    int    ret;
959
960    *rbuflen = 0;
961    ibuf += 2;
962
963    /* check if password change is allowed, OS-X ignores the flag.
964     * we shouldn't trust the client on this anyway.
965     * not sure about the "right" error code, NOOP for now */
966    if (!(obj->options.passwdbits & PASSWD_SET))
967        return AFPERR_NOOP;
968
969    /* make sure we can deal w/ this uam */
970    len = (unsigned char) *ibuf++;
971    if ((uam = auth_uamfind(UAM_SERVER_CHANGEPW, ibuf, len)) == NULL)
972        return AFPERR_BADUAM;
973
974    ibuf += len;
975    if ((len + 1) & 1) /* pad byte */
976        ibuf++;
977
978    if ( afp_version < 30) {
979        len = (unsigned char) *ibuf++;
980        if ( len > sizeof(username) - 1) {
981            return AFPERR_PARAM;
982        }
983        memcpy(username, ibuf, len);
984        username[ len ] = '\0';
985        ibuf += len;
986        if ((len + 1) & 1) /* pad byte */
987            ibuf++;
988    } else {
989        /* AFP > 3.0 doesn't pass the username, APF 3.1 specs page 124 */
990        if ( ibuf[0] != '\0' || ibuf[1] != '\0')
991            return AFPERR_PARAM;
992        ibuf += 2;
993        len = MIN(sizeof(username), strlen(obj->username));
994        memcpy(username, obj->username, len);
995        username[ len ] = '\0';
996    }
997
998
999    LOG(log_info, logtype_afpd, "changing password for <%s>", username);
1000
1001    if (( pwd = uam_getname( obj, username, sizeof(username))) == NULL )
1002        return AFPERR_PARAM;
1003
1004    /* send it off to the uam. we really don't use ibuflen right now. */
1005    if (ibuflen < (size_t)(ibuf - start))
1006        return AFPERR_PARAM;
1007
1008    ibuflen -= (ibuf - start);
1009    ret = uam->u.uam_changepw(obj, username, pwd, ibuf, ibuflen,
1010                              rbuf, rbuflen);
1011    LOG(log_info, logtype_afpd, "password change %s.",
1012        (ret == AFPERR_AUTHCONT) ? "continued" :
1013        (ret ? "failed" : "succeeded"));
1014    if ( ret == AFP_OK )
1015        set_auth_switch(0);
1016
1017    return ret;
1018}
1019
1020
1021/* FPGetUserInfo */
1022int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1023{
1024    u_int8_t  thisuser;
1025    u_int32_t id;
1026    u_int16_t bitmap;
1027    char *bitmapp;
1028
1029    LOG(log_debug, logtype_afpd, "begin afp_getuserinfo:");
1030
1031    *rbuflen = 0;
1032    ibuf++;
1033    thisuser = *ibuf++;
1034    ibuf += sizeof(id); /* userid is not used in AFP 2.0 */
1035    memcpy(&bitmap, ibuf, sizeof(bitmap));
1036    bitmap = ntohs(bitmap);
1037
1038    /* deal with error cases. we don't have to worry about
1039     * AFPERR_ACCESS or AFPERR_NOITEM as geteuid and getegid always
1040     * succeed. */
1041    if (!thisuser)
1042        return AFPERR_PARAM;
1043    if ((bitmap & USERIBIT_ALL) != bitmap)
1044        return AFPERR_BITMAP;
1045
1046    /* remember place where we store the possibly modified bitmap later */
1047    memcpy(rbuf, ibuf, sizeof(bitmap));
1048    bitmapp = rbuf;
1049    rbuf += sizeof(bitmap);
1050    *rbuflen = sizeof(bitmap);
1051
1052    /* copy the user/group info */
1053    if (bitmap & USERIBIT_USER) {
1054        id = htonl(geteuid());
1055        memcpy(rbuf, &id, sizeof(id));
1056        rbuf += sizeof(id);
1057        *rbuflen += sizeof(id);
1058    }
1059
1060    if (bitmap & USERIBIT_GROUP) {
1061        id = htonl(getegid());
1062        memcpy(rbuf, &id, sizeof(id));
1063        rbuf += sizeof(id);
1064        *rbuflen += sizeof(id);
1065    }
1066
1067    if (bitmap & USERIBIT_UUID) {
1068        if ( ! (obj->options.flags & OPTION_UUID)) {
1069            bitmap &= ~USERIBIT_UUID;
1070            bitmap = htons(bitmap);
1071            memcpy(bitmapp, &bitmap, sizeof(bitmap));
1072        } else {
1073            LOG(log_debug, logtype_afpd, "afp_getuserinfo: get UUID for \'%s\'", obj->username);
1074            int ret;
1075            atalk_uuid_t uuid;
1076            ret = getuuidfromname( obj->username, UUID_USER, uuid);
1077            if (ret != 0) {
1078                LOG(log_info, logtype_afpd, "afp_getuserinfo: error getting UUID !");
1079                return AFPERR_NOITEM;
1080            }
1081            LOG(log_debug, logtype_afpd, "afp_getuserinfo: got UUID: %s", uuid_bin2string(uuid));
1082
1083            memcpy(rbuf, uuid, UUID_BINSIZE);
1084            rbuf += UUID_BINSIZE;
1085            *rbuflen += UUID_BINSIZE;
1086        }
1087    }
1088
1089    LOG(log_debug, logtype_afpd, "END afp_getuserinfo:");
1090    return AFP_OK;
1091}
1092
1093#define UAM_LIST(type) (((type) == UAM_SERVER_LOGIN || (type) == UAM_SERVER_LOGIN_EXT) ? &uam_login : \
1094                        (((type) == UAM_SERVER_CHANGEPW) ?              \
1095                         &uam_changepw : NULL))
1096
1097/* just do a linked list search. this could be sped up with a hashed
1098 * list, but i doubt anyone's going to have enough uams to matter. */
1099struct uam_obj *auth_uamfind(const int type, const char *name,
1100                             const int len)
1101{
1102    struct uam_obj *prev, *start;
1103
1104    if (!name || !(start = UAM_LIST(type)))
1105        return NULL;
1106
1107    prev = start;
1108    while ((prev = prev->uam_prev) != start)
1109        if (strndiacasecmp(prev->uam_name, name, len) == 0)
1110            return prev;
1111
1112    return NULL;
1113}
1114
1115int auth_register(const int type, struct uam_obj *uam)
1116{
1117    struct uam_obj *start;
1118
1119    if (!uam || !uam->uam_name || (*uam->uam_name == '\0'))
1120        return -1;
1121
1122    if (!(start = UAM_LIST(type)))
1123        return 1; /* we don't know what to do with it, caller must free it */
1124
1125    uam_attach(start, uam);
1126    return 0;
1127}
1128
1129/* load all of the modules */
1130int auth_load(const char *path, const char *list)
1131{
1132    char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p;
1133    struct uam_mod *mod;
1134    struct stat st;
1135    size_t len;
1136
1137    if (!path || !*path || !list || (len = strlen(path)) > sizeof(name) - 2)
1138        return -1;
1139
1140    strlcpy(buf, list, sizeof(buf));
1141    if ((p = strtok(buf, ",")) == NULL)
1142        return -1;
1143
1144    strcpy(name, path);
1145    if (name[len - 1] != '/') {
1146        strcat(name, "/");
1147        len++;
1148    }
1149
1150    while (p) {
1151        strlcpy(name + len, p, sizeof(name) - len);
1152        LOG(log_debug, logtype_afpd, "uam: loading (%s)", name);
1153        /*
1154          if ((stat(name, &st) == 0) && (mod = uam_load(name, p))) {
1155        */
1156        if (stat(name, &st) == 0) {
1157            if ((mod = uam_load(name, p))) {
1158                uam_attach(&uam_modules, mod);
1159                LOG(log_debug, logtype_afpd, "uam: %s loaded", p);
1160            } else {
1161                LOG(log_error, logtype_afpd, "uam: %s load failure",p);
1162            }
1163        } else {
1164            LOG(log_info, logtype_afpd, "uam: uam not found (status=%d)", stat(name, &st));
1165        }
1166        p = strtok(NULL, ",");
1167    }
1168
1169    return 0;
1170}
1171
1172/* get rid of all of the uams */
1173void auth_unload(void)
1174{
1175    struct uam_mod *mod, *prev, *start = &uam_modules;
1176
1177    prev = start->uam_prev;
1178    while ((mod = prev) != start) {
1179        prev = prev->uam_prev;
1180        uam_detach(mod);
1181        uam_unload(mod);
1182    }
1183}
1184