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