]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/auth.c
Tru64 patch (Burkhard Schmidt)
[netatalk.git] / etc / afpd / auth.c
1 /*
2  * $Id: auth.c,v 1.16 2001-06-25 15:18:01 rufustfirefly Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
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 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif /* HAVE_UNISTD_H */
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <netatalk/endian.h>
21 #include <atalk/afp.h>
22 #include <atalk/compat.h>
23 #include <atalk/util.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #ifdef SHADOWPW
29 #include <shadow.h>
30 #endif /* SHADOWPW */
31
32 #include <pwd.h>
33 #include <grp.h>
34 #include <syslog.h>
35
36 #ifdef TRU64
37 #include <netdb.h>
38 #include <arpa/inet.h>
39 #include <sia.h>
40 #include <siad.h>
41
42 extern void afp_get_cmdline( int *ac, char ***av );
43 #endif /* TRU64 */
44
45 #include "globals.h"
46 #include "auth.h"
47 #include "uam_auth.h"
48 #include "switch.h"
49 #include "status.h"
50
51 int     afp_version = 11;
52 uid_t   uuid;
53 #if defined( __svr4__ ) && !defined( NGROUPS )
54 #define NGROUPS NGROUPS_MAX
55 #endif __svr4__ NGROUPS
56 #if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
57 int     groups[ NGROUPS ];
58 #else sun __svr4__ ultrix
59 #if defined( __svr4__ ) && !defined( NGROUPS )
60 #define NGROUPS NGROUPS_MAX
61 #endif __svr4__ NGROUPS
62 gid_t   groups[ NGROUPS ];
63 #endif sun ultrix
64 int     ngroups;
65
66 /*
67  * These numbers are scattered throughout the code.
68  */
69 static struct afp_versions      afp_versions[] = {
70     { "AFPVersion 1.1", 11 },
71     { "AFPVersion 2.0", 20 },
72     { "AFPVersion 2.1", 21 },
73     { "AFP2.2", 22 }
74 };
75
76 static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
77 static struct uam_obj uam_login = {"", "", 0, {{NULL}}, &uam_login,
78                                    &uam_login};
79 static struct uam_obj uam_changepw = {"", "", 0, {{NULL}}, &uam_changepw, 
80                                       &uam_changepw};
81
82 static struct uam_obj *afp_uam = NULL;
83
84
85 void status_versions( data )
86     char        *data;
87 {
88     char                *start = data;
89     u_int16_t           status;
90     int                 len, num, i;
91
92     memcpy(&status, start + AFPSTATUS_VERSOFF, sizeof(status));
93     num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ] );
94     data += ntohs( status );
95     *data++ = num;
96     for ( i = 0; i < num; i++ ) {
97         len = strlen( afp_versions[ i ].av_name );
98         *data++ = len;
99         memcpy( data, afp_versions[ i ].av_name , len );
100         data += len;
101     }
102     status = htons( data - start );
103     memcpy(start + AFPSTATUS_UAMSOFF, &status, sizeof(status));
104 }
105
106 void status_uams(char *data, const char *authlist)
107 {
108     char                *start = data;
109     u_int16_t           status;
110     struct uam_obj      *uams;
111     int                 len, num = 0;
112
113     memcpy(&status, start + AFPSTATUS_UAMSOFF, sizeof(status));
114     uams = &uam_login;
115     while ((uams = uams->uam_prev) != &uam_login) {
116       if (strstr(authlist, uams->uam_path))
117         num++;
118     }
119
120     data += ntohs( status );
121     *data++ = num;
122     while ((uams = uams->uam_prev) != &uam_login) {
123       if (strstr(authlist, uams->uam_path)) {
124         syslog(LOG_INFO, "uam: \"%s\" available", uams->uam_name);
125         len = strlen( uams->uam_name);
126         *data++ = len;
127         memcpy( data, uams->uam_name, len );
128         data += len;
129       }
130     }
131
132     /* icon offset */
133     status = htons(data - start);
134     memcpy(start + AFPSTATUS_ICONOFF, &status, sizeof(status));
135 }
136
137 /* handle errors by closing the connection. this is only needed
138  * by the afp_* functions. */
139 static int send_reply(const AFPObj *obj, const int err)
140 {
141   if ((err == AFP_OK) || (err == AFPERR_AUTHCONT))
142     return err;
143
144   obj->reply(obj->handle, err);
145   obj->exit(0);
146
147   return AFP_OK;
148 }
149
150 static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void))
151 {
152 #ifdef ADMIN_GRP
153     int admin = 0;
154 #endif ADMIN_GRP
155
156     /* UAM had syslog control; afpd needs to reassert itself */
157     openlog( "afpd", LOG_NDELAY|LOG_PID, LOG_DAEMON);
158
159     if ( pwd->pw_uid == 0 ) {   /* don't allow root login */
160         syslog( LOG_ERR, "login: root login denied!" );
161         return AFPERR_NOTAUTH;
162     }
163
164     syslog( LOG_INFO, "login %s (uid %d, gid %d)", pwd->pw_name,
165             pwd->pw_uid, pwd->pw_gid );
166
167     if (obj->proto == AFPPROTO_ASP) {
168       ASP asp = obj->handle;
169       int addr_net = ntohs( asp->asp_sat.sat_addr.s_net );
170       int addr_node  = asp->asp_sat.sat_addr.s_node;
171
172       if (obj->options.authprintdir) {
173         if(addr_net && addr_node) { /* Do we have a valid Appletalk address? */
174           char nodename[256];
175           FILE *fp;
176           struct stat stat_buf;
177
178           sprintf(nodename, "%s/net%d.%dnode%d", obj->options.authprintdir, 
179                 addr_net / 256, addr_net % 256, addr_node);
180           syslog (LOG_INFO, "registering %s (uid %d) on %u.%u as %s",
181                         pwd->pw_name, pwd->pw_uid, addr_net, addr_node, nodename);
182
183           if (stat(nodename, &stat_buf) == 0) { /* file exists */
184             if (S_ISREG(stat_buf.st_mode)) { /* normal file */
185                 unlink(nodename);
186                 fp = fopen(nodename, "w");
187                 fprintf(fp, "%s\n", pwd->pw_name);
188                 fclose(fp);
189                 chown( nodename, pwd->pw_uid, -1 );
190             } else { /* somebody is messing with us */
191                 syslog( LOG_ERR, "print authfile %s is not a normal file, it will not be modified", nodename );
192             }
193           } else { /* file 'nodename' does not exist */
194             fp = fopen(nodename, "w");
195             fprintf(fp, "%s\n", pwd->pw_name);
196             fclose(fp);
197             chown( nodename, pwd->pw_uid, -1 );
198           }
199         } /* if (addr_net && addr_node ) */
200       } /* if (options->authprintdir) */
201     } /* if (obj->proto == AFPPROTO_ASP) */
202
203     if (initgroups( pwd->pw_name, pwd->pw_gid ) < 0) {
204 #ifdef RUN_AS_USER
205       syslog(LOG_INFO, "running with uid %d", geteuid());
206 #else /* RUN_AS_USER */
207       syslog(LOG_ERR, "login: %m");
208       return AFPERR_BADUAM;
209 #endif /* RUN_AS_USER */
210
211     }
212
213     /* Basically if the user is in the admin group, we stay root */
214
215     if (( ngroups = getgroups( NGROUPS, groups )) < 0 ) {
216         syslog( LOG_ERR, "login: getgroups: %m" );
217         return AFPERR_BADUAM;
218     }
219 #ifdef ADMIN_GRP
220 #ifdef DEBUG
221     syslog(LOG_INFO, "obj->options.admingid == %d", obj->options.admingid);
222 #endif /* DEBUG */
223     if (obj->options.admingid != 0) {
224         int i;
225         for (i = 0; i < ngroups; i++) {
226            if (groups[i] == obj->options.admingid) admin = 1;
227         }
228     }
229     if (admin) syslog( LOG_INFO, "admin login -- %s", pwd->pw_name );
230     if (!admin)
231 #endif /* DEBUG */
232 #ifdef TRU64
233     {
234         struct DSI *dsi = obj->handle;
235         struct hostent *hp;
236         char *clientname;
237         int argc;
238         char **argv;
239         char hostname[256];
240
241         afp_get_cmdline( &argc, &argv );
242
243         hp = gethostbyaddr( (char *) &dsi->client.sin_addr,
244                             sizeof( struct in_addr ),
245                             dsi->client.sin_family );
246
247         if( hp )
248             clientname = hp->h_name;
249         else
250             clientname = inet_ntoa( dsi->client.sin_addr );
251
252         sprintf( hostname, "%s@%s", pwd->pw_name, clientname );
253
254         if( sia_become_user( NULL, argc, argv, hostname, pwd->pw_name,
255                              NULL, FALSE, NULL, NULL,
256                              SIA_BEU_REALLOGIN ) != SIASUCCESS )
257             return AFPERR_BADUAM;
258
259         syslog( LOG_INFO, "session from %s (%s)", hostname,
260                 inet_ntoa( dsi->client.sin_addr ) );
261
262         if (setegid( pwd->pw_gid ) < 0 || seteuid( pwd->pw_uid ) < 0) {
263             syslog( LOG_ERR, "login: %m" );
264             return AFPERR_BADUAM;
265         }
266     }
267 #else /* TRU64 */
268         if (setegid( pwd->pw_gid ) < 0 || seteuid( pwd->pw_uid ) < 0) {
269             syslog( LOG_ERR, "login: %m" );
270             return AFPERR_BADUAM;
271         }
272 #endif /* TRU64 */
273
274     /* There's probably a better way to do this, but for now, we just 
275         play root */
276
277 #ifdef ADMIN_GRP
278     if (admin) uuid = 0;
279     else
280 #endif /* ADMIN_GRP */
281     uuid = pwd->pw_uid;
282
283     afp_switch = postauth_switch;
284     obj->logout = logout;
285
286     return( AFP_OK );
287 }
288
289 int afp_login(obj, ibuf, ibuflen, rbuf, rbuflen )
290     AFPObj      *obj;
291     char        *ibuf, *rbuf;
292     int         ibuflen, *rbuflen;
293 {
294     struct passwd *pwd = NULL;
295     int         len, i, num;
296
297     *rbuflen = 0;
298
299     if ( nologin & 1) 
300         return send_reply(obj, AFPERR_SHUTDOWN );
301
302     ibuf++;
303     len = (unsigned char) *ibuf++;
304     num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ]);
305     for ( i = 0; i < num; i++ ) {
306         if ( strncmp( ibuf, afp_versions[ i ].av_name , len ) == 0 ) {
307             afp_version = afp_versions[ i ].av_number;
308             break;
309         }
310     }
311     if ( i == num )                             /* An inappropo version */
312         return send_reply(obj, AFPERR_BADVERS );
313     ibuf += len;
314
315     len = (unsigned char) *ibuf++;
316     if ((afp_uam = auth_uamfind(UAM_SERVER_LOGIN, ibuf, len)) == NULL)
317       return send_reply(obj, AFPERR_BADUAM);
318     ibuf += len;
319
320     i = afp_uam->u.uam_login.login(obj, &pwd, ibuf, ibuflen, rbuf, rbuflen);
321     if (i || !pwd) 
322       return send_reply(obj, i);
323
324     return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout));
325 }
326
327
328 int afp_logincont(obj, ibuf, ibuflen, rbuf, rbuflen)
329     AFPObj      *obj;
330     char        *ibuf, *rbuf;
331     int         ibuflen, *rbuflen;
332 {
333     struct passwd *pwd = NULL;
334     int err;
335
336     if ( afp_uam == NULL || afp_uam->u.uam_login.logincont == NULL ) {
337         *rbuflen = 0;
338         return send_reply(obj, AFPERR_NOTAUTH );
339     }
340
341     ibuf += 2;
342     err = afp_uam->u.uam_login.logincont(obj, &pwd, ibuf, ibuflen,
343                                          rbuf, rbuflen);
344     if (err || !pwd)
345       return send_reply(obj, err);
346
347     return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout));
348 }
349
350
351 int afp_logout(obj, ibuf, ibuflen, rbuf, rbuflen)
352      AFPObj     *obj;
353      char       *ibuf, *rbuf;
354      int        ibuflen, *rbuflen;
355 {
356   syslog(LOG_INFO, "logout %s", obj->username);
357   obj->exit(0);
358   return AFP_OK;
359 }
360
361
362
363 /* change password  --
364  * NOTE: an FPLogin must already have completed successfully for this
365  *       to work. this also does a little pre-processing before it hands
366  *       it off to the uam. 
367  */
368 int afp_changepw(obj, ibuf, ibuflen, rbuf, rbuflen )
369     AFPObj      *obj;
370     char        *ibuf, *rbuf;
371     int         ibuflen, *rbuflen;
372 {
373   char username[MACFILELEN + 1], *start = ibuf;
374   struct uam_obj *uam;
375   struct passwd *pwd;
376   int len;
377
378   *rbuflen = 0;
379   ibuf += 2; 
380
381   /* make sure we can deal w/ this uam */
382   len = (unsigned char) *ibuf++;
383   if ((uam = auth_uamfind(UAM_SERVER_CHANGEPW, ibuf, len)) == NULL)
384     return AFPERR_BADUAM;
385
386   ibuf += len;
387   if ((len + 1) & 1) /* pad byte */
388     ibuf++;
389
390   len = (unsigned char) *ibuf++;
391   if ( len > sizeof(username) - 1) {
392     return AFPERR_PARAM;
393   }
394   memcpy(username, ibuf, len);
395   username[ len ] = '\0';
396   ibuf += len;
397   if ((len + 1) & 1) /* pad byte */
398     ibuf++;
399   
400   syslog(LOG_INFO, "changing password for <%s>", username);
401
402   if (( pwd = uam_getname( username, sizeof(username))) == NULL )
403     return AFPERR_PARAM;
404
405   /* send it off to the uam. we really don't use ibuflen right now. */
406   ibuflen -= (ibuf - start);
407   len = uam->u.uam_changepw(obj, username, pwd, ibuf, ibuflen,
408                             rbuf, rbuflen);
409   syslog(LOG_INFO, "password change %s.", 
410          (len == AFPERR_AUTHCONT) ? "continued" :
411          (len ? "failed" : "succeeded"));
412   return len;
413 }
414
415
416 /* FPGetUserInfo */
417 int afp_getuserinfo(obj, ibuf, ibuflen, rbuf, rbuflen )
418     AFPObj      *obj;
419     char        *ibuf, *rbuf;
420     int         ibuflen, *rbuflen;
421 {
422     u_int8_t  thisuser;
423     u_int32_t id;
424     u_int16_t bitmap;
425     
426     *rbuflen = 0;
427     ibuf++;
428     thisuser = *ibuf++;
429     ibuf += sizeof(id); /* userid is not used in AFP 2.0 */
430     memcpy(&bitmap, ibuf, sizeof(bitmap));
431     bitmap = ntohs(bitmap);
432     
433     /* deal with error cases. we don't have to worry about 
434      * AFPERR_ACCESS or AFPERR_NOITEM as geteuid and getegid always
435      * succeed. */
436     if (!thisuser) 
437       return AFPERR_PARAM;
438     if ((bitmap & USERIBIT_ALL) != bitmap)
439       return AFPERR_BITMAP;
440     
441     /* copy the bitmap back to reply buffer */
442     memcpy(rbuf, ibuf, sizeof(bitmap));
443     rbuf += sizeof(bitmap);
444     *rbuflen = sizeof(bitmap);
445
446     /* copy the user/group info */
447     if (bitmap & USERIBIT_USER) {
448       id = htonl(geteuid());
449       memcpy(rbuf, &id, sizeof(id));
450       rbuf += sizeof(id);
451       *rbuflen += sizeof(id);
452     }
453     
454     if (bitmap & USERIBIT_GROUP) {
455       id = htonl(getegid());
456       memcpy(rbuf, &id, sizeof(id));
457       rbuf += sizeof(id);
458       *rbuflen += sizeof(id);
459     }
460     
461     return AFP_OK;
462 }    
463
464 #define UAM_LIST(type) (((type) == UAM_SERVER_LOGIN) ? &uam_login : \
465                         (((type) == UAM_SERVER_CHANGEPW) ? \
466                          &uam_changepw : NULL))
467
468 /* just do a linked list search. this could be sped up with a hashed
469  * list, but i doubt anyone's going to have enough uams to matter. */
470 struct uam_obj *auth_uamfind(const int type, const char *name, 
471                              const int len)
472 {
473   struct uam_obj *prev, *start;
474
475   if (!name || !(start = UAM_LIST(type)))
476     return NULL;
477
478   prev = start;
479   while ((prev = prev->uam_prev) != start) 
480     if (strndiacasecmp(prev->uam_name, name, len) == 0)
481       return prev;
482
483   return NULL;
484 }
485
486 int auth_register(const int type, struct uam_obj *uam)
487 {
488   struct uam_obj *start;
489
490   if (!uam || !uam->uam_name || (*uam->uam_name == '\0'))
491     return -1;
492
493   if (!(start = UAM_LIST(type)))
494     return 0; /* silently fail */
495
496   uam_attach(start, uam);
497   return 0;
498 }
499
500 /* load all of the modules */
501 int auth_load(const char *path, const char *list)
502 {
503   char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p; 
504   struct uam_mod *mod;
505   struct stat st;
506   int len;
507   
508   if (!path || !list || (len = strlen(path)) > sizeof(name) - 2)
509     return -1;
510
511   strncpy(buf, list, sizeof(buf));
512   if ((p = strtok(buf, ",")) == NULL)
513     return -1;
514
515   strcpy(name, path);
516   if (name[len - 1] != '/') {
517     strcat(name, "/");
518     len++;
519   }
520
521   while (p) {
522     strncpy(name + len, p, sizeof(name) - len);
523     syslog(LOG_DEBUG, "uam : Loading (%s)", name);
524     /*
525     if ((stat(name, &st) == 0) && (mod = uam_load(name, p))) {
526     */
527     if (stat(name, &st) == 0) {
528       if ((mod = uam_load(name, p))) {
529         uam_attach(&uam_modules, mod);
530         syslog(LOG_INFO, "uam: %s loaded", p);
531       } else {
532         syslog(LOG_INFO, "uam: %s load failure",p);
533       }
534     } else {
535       syslog(LOG_INFO, "uam: uam not found (status=%d)", stat(name, &st));
536     }
537     p = strtok(NULL, ",");
538   }
539
540   return 0;
541 }
542
543 /* get rid of all of the uams */
544 void auth_unload()
545 {
546   struct uam_mod *mod, *prev, *start = &uam_modules;
547
548   prev = start->uam_prev;
549   while ((mod = prev) != start) {
550     prev = prev->uam_prev;
551     uam_detach(mod);
552     uam_unload(mod);
553   }
554 }