2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/com_err.h>
17 #if defined(AFS_AIX51_ENV)
18 #include <sys/types.h>
19 #include <sys/param.h>
25 #include <sys/socket.h>
36 #include <afs/cellconfig.h>
37 #include <afs/dirpath.h>
40 #include <afs/ptserver.h>
41 #include "aix_auth_prototypes.h"
43 struct afsconf_cell ak_cellconfig; /* General information about the cell */
44 static char linkedcell[MAXCELLCHARS+1];
45 static krb5_ccache _krb425_ccache = NULL;
64 * Why doesn't AFS provide these prototypes?
67 extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
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 *);
77 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
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
85 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
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))
94 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
97 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
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
103 #elif defined(HAVE_KRB5_CREDS_SESSION)
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
110 #error "Must have either keyblock or session member of krb5_creds"
113 char *afs_realm_of_cell(krb5_context context, struct afsconf_cell *cellconfig, int fallback)
115 static char krbrlm[REALM_SZ+1];
117 krb5_error_code retval;
124 p = strchr(cellconfig->hostName[0], '.');
128 strcpy(krbrlm, cellconfig->name);
129 for (p=krbrlm; *p; p++) {
134 if (retval = krb5_get_host_realm(context,
135 cellconfig->hostName[0], &hrealms))
137 if(!hrealms[0]) return 0;
138 strcpy(krbrlm, hrealms[0]);
140 if (hrealms) krb5_free_host_realm(context, hrealms);
146 aklog_authenticate(char *userName, char *response, int *reenter, char **message)
148 char *reason, *pword, prompt[256];
150 int code, unixauthneeded, password_expires = -1;
152 krb5_context context;
154 krb5_init_context(&context);
156 *message = (char *)0;
158 status = auth_to_cell(context, userName, NULL, NULL);
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? */
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
174 if ((pwd = getpwuid(getuid())) != NULL) {
177 char fcell[100], xlog_path[512];
179 strcpy(xlog_path, pwd->pw_dir);
180 strcat(xlog_path, "/.xlog");
182 if ((stat(xlog_path, &sbuf) == 0) &&
183 ((f = fopen(xlog_path, "r")) != NULL)) {
185 while (fgets(fcell, 100, f) != NULL) {
188 fcell[strlen(fcell) - 1] = '\0';
190 auth_status = auth_to_cell(context, userName, fcell, NULL);
191 if (status == AKLOG_SUCCESS)
192 status = auth_status;
194 status = AKLOG_SOMETHINGSWRONG;
202 static krb5_error_code get_credv5(krb5_context context, char *user,
203 char *name, char *inst, char *realm,
208 static krb5_principal client_principal = 0;
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,
216 (inst && strlen(inst)) ? inst : (void *) NULL,
220 r = krb5_cc_default(context, &_krb425_ccache);
222 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: krb5_cc_default returns %d", r);
225 r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
227 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: krb5_cc_get_principal returns %d", r);
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;
235 r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
241 static int get_user_realm(krb5_context context, char *realm)
243 static krb5_principal client_principal = 0;
247 krb5_cc_default(context, &_krb425_ccache);
248 if (!client_principal)
249 krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
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);
260 aklog_chpass(char *userName, char *oldPasswd, char *newPasswd, char **message)
266 aklog_passwdexpired(char *userName, char **message)
272 aklog_passwdrestrictions(char *userName, char *newPasswd, char *oldPasswd,
279 aklog_getpasswd(char * userName)
286 static int get_cellconfig(char *cell, struct afsconf_cell *cellconfig, char *local_cell, char *linkedcell)
289 struct afsconf_dir *configdir;
291 memset(local_cell, 0, sizeof(local_cell));
292 memset((char *)cellconfig, 0, sizeof(*cellconfig));
294 if (!(configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
298 if (afsconf_GetLocalCell(configdir, local_cell, MAXCELLCHARS)) {
299 return AFSCONF_FAILURE;
302 if ((cell == NULL) || (cell[0] == 0))
305 linkedcell[0] = '\0';
306 if (afsconf_GetCellInfo(configdir, cell, NULL, cellconfig)) {
307 status = AFSCONF_NOTFOUND;
309 if (cellconfig->linkedCell)
310 strncpy(linkedcell,cellconfig->linkedCell,MAXCELLCHARS);
312 (void) afsconf_Close(configdir);
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
322 static int auth_to_cell(krb5_context context, char *user, char *cell, char *realm)
325 char username[BUFSIZ]; /* To hold client username structure */
326 afs_int32 viceId; /* AFS uid of user */
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;
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';
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);
363 strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS);
364 cell_to_use[MAXCELLCHARS] = 0;
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
372 if (realm && realm[0]) {
373 strcpy(realm_of_cell, realm);
376 char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, FALSE);
379 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
380 return AFSCONF_FAILURE;
383 strcpy(realm_of_cell, afs_realm);
386 /* We use the afs.<cellname> convention here...
388 * Doug Engert's original code had principals of the form:
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
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.
405 strcpy(name, AFSKEY);
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) {
412 secondary_instance[0] = '\0';
415 primary_instance[0] = '\0';
417 strncpy(secondary_instance, cell_to_use,
418 sizeof(secondary_instance));
419 secondary_instance[sizeof(secondary_instance)-1] = '\0';
423 * Extract the session key from the ticket file and hand-frob an
424 * afs style authenticator.
428 * Try to obtain AFS tickets. Because there are two valid service
429 * names, we will try both, but trying the more specific first.
431 * afs/<cell>@<realm> i.e. allow for single name with "."
435 status = get_credv5(context, user, name, primary_instance, realm_of_cell,
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);
443 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
444 return AFSCONF_FAILURE;
447 strcpy(realm_of_cell, afs_realm);
449 if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
451 secondary_instance[0] = '\0';
454 status = get_credv5(context, user, name, primary_instance,
455 realm_of_cell, &v5cred);
457 if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
458 status == KRB5KRB_ERR_GENERIC) {
460 status = get_credv5(context, user, name, secondary_instance,
461 realm_of_cell, &v5cred);
465 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_credv5 returns %d", status);
469 strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
470 strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
471 strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
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.
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';
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);
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);
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;
512 if (strcmp(realm_of_user, realm_of_cell)) {
513 strcat(username, "@");
514 strcat(username, realm_of_user);
517 strcpy(lastcell, aserver.cell);
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.
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);
537 * This actually only works assuming that your uid and pts space match
538 * and probably this works only for the local cell anyway.
541 if ((uid = getuid()) == 0) {
542 if ((pwd = getpwnam(user)) == NULL) {
543 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: getpwnam %s failed", user);
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));
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.
557 strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
558 strcpy(aclient.instance, "");
559 strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1);
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.
567 write(2,"",0); /* dummy write */
569 afssetpag = (getpagvalue("afs") > 0) ? 1 : 0;
571 struct sigaction newAction, origAction;
575 sigemptyset(&newAction.sa_mask);
576 newAction.sa_handler = SIG_DFL;
577 newAction.sa_flags = 0;
578 status = sigaction(SIGCHLD, &newAction, &origAction);
580 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: sigaction returned %d", status);
583 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: in daemon? forking to set tokens");
586 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog child: setting tokens");
588 status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);
590 syslog(LOG_AUTH|LOG_ERR, "LAM aklog child: set tokens, returning %d", status);
591 exit((status == 0)?0:255);
594 pcid = waitpid(cid, &wstatus, 0);
595 } while ((pcid == -1) && (errno == EINTR));
596 if ((pcid == cid) && WIFEXITED(wstatus))
597 status = WEXITSTATUS(wstatus);
601 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: collected child status %d", status);
602 sigaction(SIGCHLD, &origAction, NULL);
604 status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag);
607 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: set tokens returned %d", status);
609 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: set tokens, pag %d", getpagvalue("afs"));
614 aklog_initialize(struct secmethod_table *meths)
616 memset(meths, 0, sizeof(struct secmethod_table));
617 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog loaded: uid %d pag %d", getuid(), getpagvalue("afs"));
619 * Initialize the exported interface routines.
620 * Aside from method_authenticate, these are just no-ops.
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;
630 #endif /* AFS_AIX51_ENV */