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