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>
15 #if defined(AFS_AIX51_ENV)
16 #include <sys/types.h>
17 #include <sys/param.h>
23 #include <sys/socket.h>
34 #include <afs/cellconfig.h>
35 #include <afs/dirpath.h>
39 #include <afs/token.h>
40 #include <afs/ptserver.h>
41 #include "aix_auth_prototypes.h"
43 static int uidpag = 0;
44 static int localuid = 0;
45 struct afsconf_cell ak_cellconfig; /* General information about the cell */
46 static char linkedcell[MAXCELLCHARS+1];
47 static krb5_ccache _krb425_ccache = NULL;
66 * Why doesn't AFS provide these prototypes?
69 extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
75 static krb5_error_code get_credv5(krb5_context context, char *, char *, char *,
76 char *, krb5_creds **);
77 static int get_user_realm(krb5_context, char *);
79 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
81 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
82 #define get_princ_len(c, p, n) krb5_princ_component(c, p, n)->length
83 #define second_comp(c, p) (krb5_princ_size(c, p) > 1)
84 #define realm_data(c, p) krb5_princ_realm(c, p)->data
85 #define realm_len(c, p) krb5_princ_realm(c, p)->length
87 #elif defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
89 #define get_princ_str(c, p, n) krb5_principal_get_comp_string(c, p, n)
90 #define get_princ_len(c, p, n) strlen(krb5_principal_get_comp_string(c, p, n))
91 #define second_comp(c, p) (krb5_principal_get_comp_string(c, p, 1) != NULL)
92 #define realm_data(c, p) krb5_realm_data(krb5_principal_get_realm(c, p))
93 #define realm_len(c, p) krb5_realm_length(krb5_principal_get_realm(c, p))
96 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
99 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
101 #define get_cred_keydata(c) c->keyblock.contents
102 #define get_cred_keylen(c) c->keyblock.length
103 #define get_creds_enctype(c) c->keyblock.enctype
105 #elif defined(HAVE_KRB5_CREDS_SESSION)
107 #define get_cred_keydata(c) c->session.keyvalue.data
108 #define get_cred_keylen(c) c->session.keyvalue.length
109 #define get_creds_enctype(c) c->session.keytype
112 #error "Must have either keyblock or session member of krb5_creds"
116 afs_realm_of_cell(krb5_context context, struct afsconf_cell *cellconfig, int fallback)
118 static char krbrlm[REALM_SZ+1];
120 krb5_error_code retval;
127 p = strchr(cellconfig->hostName[0], '.');
131 strcpy(krbrlm, cellconfig->name);
132 for (p=krbrlm; *p; p++) {
137 if (retval = krb5_get_host_realm(context,
138 cellconfig->hostName[0], &hrealms))
140 if(!hrealms[0]) return 0;
141 strcpy(krbrlm, hrealms[0]);
143 if (hrealms) krb5_free_host_realm(context, hrealms);
149 aklog_authenticate(char *userName, char *response, int *reenter, char **message)
151 char *reason, *pword, prompt[256];
153 int code, unixauthneeded, password_expires = -1;
155 krb5_context context;
157 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: uidpag %s localuid %s",
158 uidpag ? "yes" : "no",
159 localuid ? "yes" : "no");
161 krb5_init_context(&context);
163 *message = (char *)0;
165 status = auth_to_cell(context, userName, NULL, NULL);
168 char *str = afs_error_message(status);
169 *message = malloc(1024);
170 #ifdef HAVE_KRB5_SVC_GET_MSG
171 if (strncmp(str, "unknown", strlen("unknown")) == 0) {
172 krb5_svc_get_msg(status,&str);
173 sprintf(*message, "Unable to obtain AFS tokens: %s.\n",
175 krb5_free_string(context, str);
178 sprintf(*message, "Unable to obtain AFS tokens: %s.\n",
180 return AUTH_FAILURE; /* NOTFOUND? */
185 static krb5_error_code
186 get_credv5(krb5_context context, char *user,
187 char *name, char *inst, char *realm,
192 static krb5_principal client_principal = 0;
195 memset(&increds, 0, sizeof(increds));
196 /* instance may be ptr to a null string. Pass null then */
197 if ((r = krb5_build_principal(context, &increds.server,
198 strlen(realm), realm,
200 (inst && strlen(inst)) ? inst : NULL,
204 r = krb5_cc_default(context, &_krb425_ccache);
206 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: krb5_cc_default returns %d", r);
209 r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
211 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: krb5_cc_get_principal returns %d", r);
214 increds.client = client_principal;
215 increds.times.endtime = 0;
216 /* Ask for DES since that is what V4 understands */
217 get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC;
219 r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
226 get_user_realm(krb5_context context, char *realm)
228 static krb5_principal client_principal = 0;
232 krb5_cc_default(context, &_krb425_ccache);
233 if (!client_principal)
234 krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
236 i = realm_len(context, client_principal);
237 if (i > REALM_SZ-1) i = REALM_SZ-1;
238 strncpy(realm,realm_data(context, client_principal), i);
245 aklog_chpass(char *userName, char *oldPasswd, char *newPasswd, char **message)
251 aklog_passwdexpired(char *userName, char **message)
257 aklog_passwdrestrictions(char *userName, char *newPasswd, char *oldPasswd,
264 aklog_getpasswd(char * userName)
271 aklog_open(char *name, char *domain, int mode, char *options)
275 /* I can't find this documented anywhere, but it looks like the "options"
276 * string is NUL-delimited (so NULs replace commas in the configuration
277 * file), and the end of the list is marked by an extra NUL. */
279 for (opt = options; opt && *opt; opt += strlen(opt) + 1) {
280 if (strcasecmp(opt, "uidpag") == 0) {
283 if (strcasecmp(opt, "localuid") == 0) {
291 get_cellconfig(char *cell, struct afsconf_cell *cellconfig, char *local_cell, char *linkedcell)
294 struct afsconf_dir *configdir;
296 memset(local_cell, 0, sizeof(local_cell));
297 memset(cellconfig, 0, sizeof(*cellconfig));
299 if (!(configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
303 if (afsconf_GetLocalCell(configdir, local_cell, MAXCELLCHARS)) {
304 return AFSCONF_FAILURE;
307 if ((cell == NULL) || (cell[0] == 0))
310 linkedcell[0] = '\0';
311 if (afsconf_GetCellInfo(configdir, cell, NULL, cellconfig)) {
312 status = AFSCONF_NOTFOUND;
314 if (cellconfig->linkedCell)
315 strncpy(linkedcell,cellconfig->linkedCell,MAXCELLCHARS);
317 (void) afsconf_Close(configdir);
323 * Log to a cell. If the cell has already been logged to, return without
324 * doing anything. Otherwise, log to it and mark that it has been logged
328 auth_to_cell(krb5_context context, char *user, char *cell, char *realm)
331 char username[BUFSIZ]; /* To hold client username structure */
332 afs_int32 viceId; /* AFS uid of user */
334 char name[ANAME_SZ]; /* Name of afs key */
335 char primary_instance[INST_SZ]; /* Instance of afs key */
336 char secondary_instance[INST_SZ]; /* Backup instance to try */
337 int try_secondary = 0; /* Flag to indicate if we try second */
338 char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
339 char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
340 char local_cell[MAXCELLCHARS+1];
341 char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */
342 static char confname[512] = { 0 };
343 krb5_creds *v5cred = NULL;
344 struct ktc_principal aserver;
345 int afssetpag = 0, uid = -1;
346 struct passwd *pwd = NULL;
347 struct ktc_setTokenData *token = NULL;
348 struct ktc_tokenUnion *rxkadToken = NULL;
350 memset(name, 0, sizeof(name));
351 memset(primary_instance, 0, sizeof(primary_instance));
352 memset(secondary_instance, 0, sizeof(secondary_instance));
353 memset(realm_of_user, 0, sizeof(realm_of_user));
354 memset(realm_of_cell, 0, sizeof(realm_of_cell));
355 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog starting: user %s uid %d", user, getuid());
356 if (confname[0] == '\0') {
357 strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname));
358 confname[sizeof(confname) - 2] = '\0';
361 /* NULL or empty cell returns information on local cell */
362 if ((status = get_cellconfig(cell, &ak_cellconfig,
363 local_cell, linkedcell))) {
364 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_cellconfig returns %d", status);
368 strncpy(cell_to_use, ak_cellconfig.name, MAXCELLCHARS);
369 cell_to_use[MAXCELLCHARS] = 0;
372 * Find out which realm we're supposed to authenticate to. If one
373 * is not included, use the kerberos realm found in the credentials
377 if (realm && realm[0]) {
378 strcpy(realm_of_cell, realm);
381 char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, FALSE);
384 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
385 return AFSCONF_FAILURE;
388 strcpy(realm_of_cell, afs_realm);
391 /* We use the afs.<cellname> convention here...
393 * Doug Engert's original code had principals of the form:
397 * in the KDC, so the name wouldn't conflict with DFS. Since we're
398 * not using DFS, I changed it just to look for the following
404 * Because people are transitioning from afs@realm to afs/cell,
405 * we configure things so that if the first one isn't found, we
406 * try the second one. You can select which one you prefer with
407 * a configure option.
410 strcpy(name, AFSKEY);
412 if (1 || strcasecmp(cell_to_use, realm_of_cell) != 0) {
413 strncpy(primary_instance, cell_to_use, sizeof(primary_instance));
414 primary_instance[sizeof(primary_instance)-1] = '\0';
415 if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
417 secondary_instance[0] = '\0';
420 primary_instance[0] = '\0';
422 strncpy(secondary_instance, cell_to_use,
423 sizeof(secondary_instance));
424 secondary_instance[sizeof(secondary_instance)-1] = '\0';
427 token = token_buildTokenJar(ak_cellconfig.name);
429 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: token_buildTokenJar returns NULL");
434 * Extract the session key from the ticket file and hand-frob an
435 * afs style authenticator.
439 * Try to obtain AFS tickets. Because there are two valid service
440 * names, we will try both, but trying the more specific first.
442 * afs/<cell>@<realm> i.e. allow for single name with "."
446 status = get_credv5(context, user, name, primary_instance, realm_of_cell,
449 if ((status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
450 status == KRB5KRB_ERR_GENERIC) && !realm_of_cell[0]) {
451 char *afs_realm = afs_realm_of_cell(context, &ak_cellconfig, TRUE);
454 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: afs_realm_of_cell returns %d", status);
455 status = AFSCONF_FAILURE;
459 strcpy(realm_of_cell, afs_realm);
461 if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
463 secondary_instance[0] = '\0';
466 status = get_credv5(context, user, name, primary_instance,
467 realm_of_cell, &v5cred);
469 if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN ||
470 status == KRB5KRB_ERR_GENERIC) {
472 status = get_credv5(context, user, name, secondary_instance,
473 realm_of_cell, &v5cred);
477 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_credv5 returns %d", status);
481 strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
482 strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
483 strncpy(aserver.cell, cell_to_use, MAXKTCREALMLEN - 1);
486 * The default is to use rxkad2b, which means we put in a full
487 * V5 ticket. If the user specifies -524, we talk to the
488 * 524 ticket converter.
494 struct ktc_token atoken;
496 len = min(get_princ_len(context, v5cred->client, 0),
497 second_comp(context, v5cred->client) ?
498 MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1);
499 strncpy(username, get_princ_str(context, v5cred->client, 0), len);
500 username[len] = '\0';
502 if (second_comp(context, v5cred->client)) {
503 strcat(username, ".");
504 p = username + strlen(username);
505 len = min(get_princ_len(context, v5cred->client, 1),
506 MAXKTCNAMELEN - strlen(username) - 1);
507 strncpy(p, get_princ_str(context, v5cred->client, 1), len);
511 memset(&atoken, 0, sizeof(atoken));
512 atoken.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
513 atoken.startTime = v5cred->times.starttime;;
514 atoken.endTime = v5cred->times.endtime;
515 memcpy(&atoken.sessionKey, get_cred_keydata(v5cred),
516 get_cred_keylen(v5cred));
517 atoken.ticketLen = v5cred->ticket.length;
518 memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
520 status = token_importRxkadViceId(&rxkadToken, &atoken, 0);
522 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: importRxkad failed with %d", status);
527 status = token_addToken(token, rxkadToken);
529 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: addToken failed with %d", status);
533 if ((status = get_user_realm(context, realm_of_user))) {
534 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: get_user_realm returns %d", status);
535 status = KRB5_REALM_UNKNOWN;
538 if (strcmp(realm_of_user, realm_of_cell)) {
539 strcat(username, "@");
540 strcat(username, realm_of_user);
545 pwd = getpwnam(user);
547 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: getpwnam %s failed", user);
548 status = AUTH_FAILURE;
553 viceId = ANONYMOUSID;
556 /* This actually crashes long-running daemons */
557 if (!pr_Initialize (0, confname, aserver.cell))
558 status = pr_SNameToId (username, &viceId);
560 viceId = ANONYMOUSID;
563 * This actually only works assuming that your uid and pts space match
564 * and probably this works only for the local cell anyway.
568 viceId = pwd->pw_uid;
574 /* Don't do first-time registration. Handle only the simple case */
575 if ((status == 0) && (viceId != ANONYMOUSID)) {
576 status = token_setRxkadViceId(rxkadToken, viceId);
578 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: Error %d setting rxkad ViceId", status);
581 token_replaceToken(token, &rxkadToken);
582 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: setting tokens for ViceId %d\n",
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.
593 write(2,"",0); /* dummy write */
596 /* If uidpag is set, we want to use UID-based PAGs, and not real PAGs, so
597 * don't create a new PAG if we're not in a PAG. But if uidpag is _not_
598 * set, just always create a new PAG. */
600 afssetpag = (getpagvalue("afs") > 0) ? 1 : 0;
602 /* we can't set a UID PAG if we're already in a real PAG. Whine */
603 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: uidpag is set, but we are "
604 "already in a PAG; this cannot work! "
605 "Attempting to continue by creating a "
611 token_setPag(token, afssetpag);
613 if (uid == 0 && uidpag) {
614 /* We are root, and we want to use UID-based PAGs. So, fork a child
615 * and setuid before setting tokens, so we set the tokens for the
617 struct sigaction newAction, origAction;
621 sigemptyset(&newAction.sa_mask);
622 newAction.sa_handler = SIG_DFL;
623 newAction.sa_flags = 0;
624 status = sigaction(SIGCHLD, &newAction, &origAction);
626 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: sigaction returned %d", status);
627 status = AUTH_FAILURE;
630 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: in daemon? forking to set tokens");
633 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog child: setting tokens");
635 status = ktc_SetTokenEx(token);
637 syslog(LOG_AUTH|LOG_ERR, "LAM aklog child: set tokens, returning %d", status);
638 exit((status == 0)?0:255);
641 pcid = waitpid(cid, &wstatus, 0);
642 } while ((pcid == -1) && (errno == EINTR));
643 if ((pcid == cid) && WIFEXITED(wstatus))
644 status = WEXITSTATUS(wstatus);
648 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: collected child status %d", status);
649 sigaction(SIGCHLD, &origAction, NULL);
651 status = ktc_SetTokenEx(token);
654 syslog(LOG_AUTH|LOG_ERR, "LAM aklog: set tokens returned %d", status);
656 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog: set tokens, pag %d", getpagvalue("afs"));
660 token_freeToken(&rxkadToken);
662 token_FreeSet(&token);
667 aklog_initialize(struct secmethod_table *meths)
669 memset(meths, 0, sizeof(struct secmethod_table));
670 syslog(LOG_AUTH|LOG_DEBUG, "LAM aklog loaded: uid %d pag %d", getuid(), getpagvalue("afs"));
672 * Initialize the exported interface routines.
673 * Aside from method_authenticate and method_open, these are just no-ops.
675 meths->method_chpass = aklog_chpass;
676 meths->method_authenticate = aklog_authenticate;
677 meths->method_passwdexpired = aklog_passwdexpired;
678 meths->method_passwdrestrictions = aklog_passwdrestrictions;
679 meths->method_getpasswd = aklog_getpasswd;
680 meths->method_open = aklog_open;
684 #endif /* AFS_AIX51_ENV */