aix-lam-aklog-update-20080103
[openafs.git] / src / tsm41 / aix_aklog.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14 ("$Header$");
15
16 #if defined(AFS_AIX51_ENV)
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <stdio.h>
20 #include <locale.h>
21 #include <nl_types.h>
22 #include <pwd.h>
23 #include <netdb.h>
24 #include <sys/socket.h>
25 #include <sys/file.h>
26 #include <sys/pag.h>
27 #include <errno.h>
28 #include <usersec.h>
29 #include <syslog.h>
30
31 #include <krb5.h>
32
33 #include <afs/cellconfig.h>
34 #include <afs/dirpath.h>
35 #include <rx/rxkad.h>
36 #include <afs/auth.h>
37 #include <afs/ptserver.h>
38 #include "aix_auth_prototypes.h"
39
40 struct afsconf_cell ak_cellconfig; /* General information about the cell */
41 static char linkedcell[MAXCELLCHARS+1];
42 static krb5_ccache  _krb425_ccache = NULL;
43
44 #define AFSKEY "afs"
45 #define AFSINST ""
46
47 #ifndef ANAME_SZ
48 #define ANAME_SZ 40
49 #endif /* ANAME_SZ */
50 #ifndef REALM_SZ
51 #define REALM_SZ 40
52 #endif /* REALM_SZ */
53 #ifndef SNAME_SZ
54 #define SNAME_SZ 40
55 #endif /* SNAME_SZ */
56 #ifndef INST_SZ
57 #define INST_SZ 40
58 #endif /* INST_SZ */
59
60 /*
61  * Why doesn't AFS provide these prototypes?
62  */
63
64 extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
65
66 /*
67  * Other prototypes
68  */
69
70 static krb5_error_code get_credv5(krb5_context context, char *, char *, char *,
71                                   char *, krb5_creds **);
72 static int get_user_realm(krb5_context, char *);
73
74 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
75
76 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
77 #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length
78 #define second_comp(c, p) (krb5_princ_size(c, p) > 1)
79 #define realm_data(c, p) krb5_princ_realm(c, p)->data
80 #define realm_len(c, p) krb5_princ_realm(c, p)->length
81
82 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
83
84 #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n)
85 #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n))
86 #define second_comp(c, p) (krb5_principal_get_comp_string(c, p, 1) != NULL)
87 #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p))
88 #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p))
89
90 #else
91 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
92 #endif
93
94 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
95
96 #define get_cred_keydata(c) c->keyblock.contents
97 #define get_cred_keylen(c) c->keyblock.length
98 #define get_creds_enctype(c) c->keyblock.enctype
99
100 #elif defined(HAVE_KRB5_CREDS_SESSION)
101
102 #define get_cred_keydata(c) c->session.keyvalue.data
103 #define get_cred_keylen(c) c->session.keyvalue.length
104 #define get_creds_enctype(c) c->session.keytype
105
106 #else
107 #error "Must have either keyblock or session member of krb5_creds"
108 #endif
109
110 char *afs_realm_of_cell(krb5_context context, struct afsconf_cell *cellconfig, int fallback)
111 {
112     static char krbrlm[REALM_SZ+1];
113     char **hrealms = 0;
114     krb5_error_code retval;
115     
116     if (!cellconfig)
117         return 0;
118     
119     if (fallback) {
120         char * p;
121         p = strchr(cellconfig->hostName[0], '.');
122         if (p++)
123             strcpy(krbrlm, p);
124         else
125             strcpy(krbrlm, cellconfig->name);
126         for (p=krbrlm; *p; p++) {
127             if (islower(*p)) 
128                 *p = toupper(*p);
129         }
130     } else {
131         if (retval = krb5_get_host_realm(context,
132                                          cellconfig->hostName[0], &hrealms))
133             return 0; 
134         if(!hrealms[0]) return 0;
135         strcpy(krbrlm, hrealms[0]);
136         
137         if (hrealms) krb5_free_host_realm(context, hrealms);
138     }
139     return krbrlm;
140 }
141
142 int
143 aklog_authenticate(char *userName, char *response, int *reenter, char **message)
144 {
145     char *reason, *pword, prompt[256];
146     struct passwd *pwd;
147     int code, unixauthneeded, password_expires = -1;
148     int status;
149     krb5_context context;
150     
151     krb5_init_context(&context);
152     *reenter = 0;
153     *message = (char *)0;
154     
155     status = auth_to_cell(context, userName, NULL, NULL);
156     
157     if (status) {
158         *message = (char *)malloc(1024);
159         sprintf(*message, "Unable to obtain AFS tokens: %s.\n",
160                 afs_error_message(status));
161         return AUTH_FAILURE; /* NOTFOUND? */
162     }
163     
164 #if 0
165     /*
166      * Local hack - if the person has a file in their home
167      * directory called ".xlog", read that for a list of
168      * extra cells to authenticate to
169      */
170     
171     if ((pwd = getpwuid(getuid())) != NULL) {
172         struct stat sbuf;
173         FILE *f;
174         char fcell[100], xlog_path[512];
175         
176         strcpy(xlog_path, pwd->pw_dir);
177         strcat(xlog_path, "/.xlog");
178         
179         if ((stat(xlog_path, &sbuf) == 0) &&
180             ((f = fopen(xlog_path, "r")) != NULL)) {
181             
182             while (fgets(fcell, 100, f) != NULL) {
183                 int auth_status;
184                 
185                 fcell[strlen(fcell) - 1] = '\0';
186                 
187                 auth_status = auth_to_cell(context, userName, fcell, NULL);
188                 if (status == AKLOG_SUCCESS)
189                     status = auth_status;
190                 else
191                     status = AKLOG_SOMETHINGSWRONG;
192             }
193         }
194     }
195 #endif
196     return AUTH_SUCCESS;
197 }
198
199 static krb5_error_code get_credv5(krb5_context context, char *user,  
200                                   char *name, char *inst, char *realm,
201                                   krb5_creds **creds)
202 {
203     krb5_creds increds;
204     krb5_error_code r;
205     static krb5_principal client_principal = 0;
206     char *str;
207     
208     memset((char *)&increds, 0, sizeof(increds));
209     /* instance may be ptr to a null string. Pass null then */
210     if ((r = krb5_build_principal(context, &increds.server,
211                                   strlen(realm), realm,
212                                   name,
213                                   (inst && strlen(inst)) ? inst : (void *) NULL,
214                                   (void *) NULL))) {
215         return r;
216     }
217     r = krb5_cc_default(context, &_krb425_ccache);
218     if (r) {
219         syslog(LOG_AUTH|LOG_ERR, "LAM aklog: krb5_cc_default returns %d", r);
220         return r;
221     }
222     r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal); 
223     if (r) {
224         syslog(LOG_AUTH|LOG_ERR, "LAM aklog: krb5_cc_get_principal returns %d", r);
225         return r;
226     } 
227     increds.client = client_principal;
228     increds.times.endtime = 0;
229     /* Ask for DES since that is what V4 understands */
230     get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC;
231     
232     r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
233     
234     return r;
235 }
236
237
238 static int get_user_realm(krb5_context context, char *realm)
239 {
240     static krb5_principal client_principal = 0;
241     int i;
242     
243     if (!_krb425_ccache)
244         krb5_cc_default(context, &_krb425_ccache);
245     if (!client_principal)
246         krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
247     
248     i = realm_len(context, client_principal);
249     if (i > REALM_SZ-1) i = REALM_SZ-1;
250     strncpy(realm,realm_data(context, client_principal), i);
251     realm[i] = 0;
252     
253     return 0;
254 }
255
256 int
257 aklog_chpass(char *userName, char *oldPasswd, char *newPasswd, char **message)
258 {
259     return AUTH_SUCCESS;
260 }
261
262 int
263 aklog_passwdexpired(char *userName, char **message)
264 {
265     return AUTH_SUCCESS;
266 }
267
268 int
269 aklog_passwdrestrictions(char *userName, char *newPasswd, char *oldPasswd,
270                          char **message)
271 {
272     return AUTH_SUCCESS;
273 }
274
275 char *
276 aklog_getpasswd(char * userName)
277 {
278     errno = ENOSYS;
279     return NULL;
280 }
281
282
283 static int get_cellconfig(char *cell, struct afsconf_cell *cellconfig, char *local_cell, char *linkedcell)
284 {
285     int status = 0;
286     struct afsconf_dir *configdir;
287     
288     memset(local_cell, 0, sizeof(local_cell));
289     memset((char *)cellconfig, 0, sizeof(*cellconfig));
290     
291     if (!(configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
292         return AFSCONF_NODB;
293     }
294     
295     if (afsconf_GetLocalCell(configdir, local_cell, MAXCELLCHARS)) {
296         return AFSCONF_FAILURE;
297     }
298     
299     if ((cell == NULL) || (cell[0] == 0))
300         cell = local_cell;
301     
302     linkedcell[0] = '\0';
303     if (afsconf_GetCellInfo(configdir, cell, NULL, cellconfig)) {
304         status = AFSCONF_NOTFOUND;
305     }
306     if (cellconfig->linkedCell) 
307         strncpy(linkedcell,cellconfig->linkedCell,MAXCELLCHARS);
308     
309     (void) afsconf_Close(configdir);
310     
311     return(status);
312 }
313
314 /* 
315  * Log to a cell.  If the cell has already been logged to, return without
316  * doing anything.  Otherwise, log to it and mark that it has been logged
317  * to.
318  */
319 static int auth_to_cell(krb5_context context, char *user, char *cell, char *realm)
320 {
321     int status = 0;
322     char username[BUFSIZ];      /* To hold client username structure */
323     afs_int32 viceId;           /* AFS uid of user */
324     
325     char name[ANAME_SZ];        /* Name of afs key */
326     char primary_instance[INST_SZ];     /* Instance of afs key */
327     char secondary_instance[INST_SZ];   /* Backup instance to try */
328     int try_secondary = 0;              /* Flag to indicate if we try second */
329     char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
330     char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
331     char local_cell[MAXCELLCHARS+1];
332     char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */
333     static char lastcell[MAXCELLCHARS+1] = { 0 };
334     static char confname[512] = { 0 };
335     krb5_creds *v5cred = NULL;
336     struct ktc_principal aserver;
337     struct ktc_principal aclient;
338     struct ktc_token atoken, btoken;
339     int afssetpag = 0, uid = -1;
340     struct passwd *pwd;
341     
342     memset(name, 0, sizeof(name));
343     memset(primary_instance, 0, sizeof(primary_instance));
344     memset(secondary_instance, 0, sizeof(secondary_instance));
345     memset(realm_of_user, 0, sizeof(realm_of_user));
346     memset(realm_of_cell, 0, sizeof(realm_of_cell));
347     syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog starting: user %s uid %d", user, getuid());
348     if (confname[0] == '\0') {
349         strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname));
350         confname[sizeof(confname) - 2] = '\0';
351     }
352     
353     /* NULL or empty cell returns information on local cell */
354     if ((status = get_cellconfig(cell, &ak_cellconfig,
355                                  local_cell, linkedcell))) {
356         syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_cellconfig returns %d", status);
357         return(status);
358     }
359     
360     strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS);
361     cell_to_use[MAXCELLCHARS] = 0;
362     
363     /*
364      * Find out which realm we're supposed to authenticate to.  If one
365      * is not included, use the kerberos realm found in the credentials
366      * cache.
367      */
368     
369     if (realm && realm[0]) {
370         strcpy(realm_of_cell, realm);
371     }
372     else {
373         char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, FALSE);
374         
375         if (!afs_realm) {
376             syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
377             return AFSCONF_FAILURE;
378         }
379         
380         strcpy(realm_of_cell, afs_realm);
381     }
382     
383     /* We use the afs.<cellname> convention here... 
384      *
385      * Doug Engert's original code had principals of the form:
386      *
387      * "afsx/cell@realm"
388      *
389      * in the KDC, so the name wouldn't conflict with DFS.  Since we're
390      * not using DFS, I changed it just to look for the following
391      * principals:
392      *
393      * afs/<cell>@<realm>
394      * afs@<realm>
395      *
396      * Because people are transitioning from afs@realm to afs/cell,
397      * we configure things so that if the first one isn't found, we
398      * try the second one.  You can select which one you prefer with
399      * a configure option.
400      */
401     
402     strcpy(name, AFSKEY);
403     
404     if (1 || strcasecmp(cell_to_use, realm_of_cell) != 0) {
405         strncpy(primary_instance, cell_to_use, sizeof(primary_instance));
406         primary_instance[sizeof(primary_instance)-1] = '\0';
407         if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
408             try_secondary = 1;
409             secondary_instance[0] = '\0';
410         }
411     } else {
412         primary_instance[0] = '\0';
413         try_secondary = 1;
414         strncpy(secondary_instance, cell_to_use,
415                 sizeof(secondary_instance));
416         secondary_instance[sizeof(secondary_instance)-1] = '\0';
417     }
418     
419     /* 
420      * Extract the session key from the ticket file and hand-frob an
421      * afs style authenticator.
422      */
423     
424     /*
425      * Try to obtain AFS tickets.  Because there are two valid service
426      * names, we will try both, but trying the more specific first.
427      *
428      *  afs/<cell>@<realm> i.e. allow for single name with "."
429      *  afs@<realm>
430      */
431     
432     status = get_credv5(context, user, name, primary_instance, realm_of_cell,
433                         &v5cred);
434     
435     if ((status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || 
436          status == KRB5KRB_ERR_GENERIC) && !realm_of_cell[0]) {
437         char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, TRUE);
438         
439         if (!afs_realm) {
440             syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
441             return AFSCONF_FAILURE;
442         }
443         
444         strcpy(realm_of_cell, afs_realm);
445         
446         if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
447             try_secondary = 1;
448             secondary_instance[0] = '\0';
449         }
450         
451         status = get_credv5(context, user, name, primary_instance, 
452                             realm_of_cell, &v5cred);
453     }
454     if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || 
455         status == KRB5KRB_ERR_GENERIC) {
456         if (try_secondary)
457             status = get_credv5(context, user, name, secondary_instance,
458                                 realm_of_cell, &v5cred);
459     }
460     
461     if (status) {
462         syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_credv5 returns %d", status);
463         return status;
464     }
465     
466     strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
467     strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
468     strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
469     
470     /*
471      * The default is to use rxkad2b, which means we put in a full
472      * V5 ticket.  If the user specifies -524, we talk to the
473      * 524 ticket converter.
474      */
475     
476     {
477         char *p;
478         int len;
479         
480         len = min(get_princ_len(context, v5cred->client, 0),
481                   second_comp(context, v5cred->client) ?
482                   MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1);
483         strncpy(username, get_princ_str(context, v5cred->client, 0), len);
484         username[len] = '\0';
485         
486         if (second_comp(context, v5cred->client)) {
487             strcat(username, ".");
488             p = username + strlen(username);
489             len = min(get_princ_len(context, v5cred->client, 1),
490                       MAXKTCNAMELEN - strlen(username) - 1);
491             strncpy(p, get_princ_str(context, v5cred->client, 1), len);
492             p[len] = '\0';
493         }
494         
495         memset(&atoken, 0, sizeof(atoken));
496         atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
497         atoken.startTime = v5cred->times.starttime;;
498         atoken.endTime = v5cred->times.endtime;
499         memcpy(&atoken.sessionKey, get_cred_keydata(v5cred),
500                get_cred_keylen(v5cred));
501         atoken.ticketLen = v5cred->ticket.length;
502         memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
503     }
504     
505     if ((status = get_user_realm(context, realm_of_user))) {
506         syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_user_realm returns %d", status);
507         return KRB5_REALM_UNKNOWN;
508     }
509     if (strcmp(realm_of_user, realm_of_cell)) {
510         strcat(username, "@");
511         strcat(username, realm_of_user);
512     }
513     
514     strcpy(lastcell, aserver.cell);
515
516     /*
517      * This is a crock, but it is Transarc's crock, so
518      * we have to play along in order to get the
519      * functionality.  The way the afs id is stored is
520      * as a string in the username field of the token.
521      * Contrary to what you may think by looking at
522      * the code for tokens, this hack (AFS ID %d) will
523      * not work if you change %d to something else.
524      */
525
526 #if 0
527     /* This actually crashes long-running daemons */
528     if (!pr_Initialize (0, confname, aserver.cell))
529         status = pr_SNameToId (username, &viceId);
530     if ((status == 0) && (viceId != ANONYMOUSID))
531         sprintf (username, "AFS ID %d", (int) viceId);
532 #else
533     /* 
534      * This actually only works assuming that your uid and pts space match 
535      * and probably this works only for the local cell anyway.
536      */
537     
538     if ((uid = getuid()) == 0) {
539         if ((pwd = getpwnam(user)) == NULL) {
540             syslog(LOG_AUTH|LOG_ERR, "LAM aklog: getpwnam %s failed", user);
541             return AUTH_FAILURE;
542         }
543     }
544     
545     /* Don't do first-time registration. Handle only the simple case */
546     if ((status == 0) && (viceId != ANONYMOUSID)) 
547         sprintf (username, "AFS ID %d", ((uid==0)?(int)pwd->pw_uid:(int)uid));
548 #endif
549
550     /* Reset the "aclient" structure before we call ktc_SetToken.
551      * This structure was first set by the ktc_GetToken call when
552      * we were comparing whether identical tokens already existed.
553      */
554     strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
555     strcpy(aclient.instance, "");
556     strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1);
557     
558 #ifndef AFS_AIX51_ENV
559     /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before 
560      * this routine, it will not add the token. It is not clear what 
561      * is going on here! So we will do the following operation.
562      * On AIX 5 this kills our parent. So we won't.
563      */
564     write(2,"",0); /* dummy write */
565 #endif
566     afssetpag = (getpagvalue("afs") > 0) ? 1 : 0;
567     if (uid == 0) {
568         struct sigaction newAction, origAction;
569         pid_t cid, pcid;
570         int wstatus;
571         
572         sigemptyset(&newAction.sa_mask);
573         newAction.sa_handler = SIG_DFL;
574         newAction.sa_flags = 0;
575         status = sigaction(SIGCHLD, &newAction, &origAction);
576         if (status) {
577             syslog(LOG_AUTH|LOG_ERR, "LAM aklog: sigaction returned %d", status);
578             return AUTH_FAILURE;
579         }
580         syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: in daemon? forking to set tokens");
581         cid = fork();
582         if (cid <= 0) {
583             syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog child: setting tokens");
584             setuid(pwd->pw_uid);
585             status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);
586             if (status != 0)
587                 syslog(LOG_AUTH|LOG_ERR, "LAM aklog child: set tokens, returning %d", status);
588             exit((status == 0)?0:255);
589         } else {
590             do {
591                 pcid = waitpid(cid, &wstatus, 0);
592             } while ((pcid == -1) && (errno == EINTR));
593             if ((pcid == cid) && WIFEXITED(wstatus))
594                 status = WEXITSTATUS(wstatus);
595             else 
596                 status = -1;
597         } 
598         syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: collected child status %d", status);
599         sigaction(SIGCHLD, &origAction, NULL);
600     } else {
601         status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);
602     }
603     if (status != 0)
604         syslog(LOG_AUTH|LOG_ERR, "LAM aklog: set tokens returned %d", status);
605     else
606         syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: set tokens, pag %d", getpagvalue("afs"));
607     return(status);
608 }
609
610 int
611 aklog_initialize(struct secmethod_table *meths)
612 {
613     memset(meths, 0, sizeof(struct secmethod_table));
614     syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog loaded: uid %d pag %d", getuid(), getpagvalue("afs"));    
615     /*
616      * Initialize the exported interface routines.
617      * Aside from method_authenticate, these are just no-ops.
618      */
619     meths->method_chpass = aklog_chpass;
620     meths->method_authenticate = aklog_authenticate;
621     meths->method_passwdexpired = aklog_passwdexpired;
622     meths->method_passwdrestrictions = aklog_passwdrestrictions;
623     meths->method_getpasswd = aklog_getpasswd;
624     
625     return (0);
626 }
627 #endif /* AFS_AIX51_ENV */