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