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 * Local hack - if the person has a file in their home
186 * directory called ".xlog", read that for a list of
187 * extra cells to authenticate to
190 if ((pwd
= getpwuid(getuid())) != NULL
) {
193 char fcell
[100], xlog_path
[512];
195 strcpy(xlog_path
, pwd
->pw_dir
);
196 strcat(xlog_path
, "/.xlog");
198 if ((stat(xlog_path
, &sbuf
) == 0) &&
199 ((f
= fopen(xlog_path
, "r")) != NULL
)) {
201 while (fgets(fcell
, 100, f
) != NULL
) {
204 fcell
[strlen(fcell
) - 1] = '\0';
206 auth_status
= auth_to_cell(context
, userName
, fcell
, NULL
);
207 if (status
== AKLOG_SUCCESS
)
208 status
= auth_status
;
210 status
= AKLOG_SOMETHINGSWRONG
;
218 static krb5_error_code
219 get_credv5(krb5_context context
, char *user
,
220 char *name
, char *inst
, char *realm
,
225 static krb5_principal client_principal
= 0;
228 memset(&increds
, 0, sizeof(increds
));
229 /* instance may be ptr to a null string. Pass null then */
230 if ((r
= krb5_build_principal(context
, &increds
.server
,
231 strlen(realm
), realm
,
233 (inst
&& strlen(inst
)) ? inst
: NULL
,
237 r
= krb5_cc_default(context
, &_krb425_ccache
);
239 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: krb5_cc_default returns %d", r
);
242 r
= krb5_cc_get_principal(context
, _krb425_ccache
, &client_principal
);
244 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: krb5_cc_get_principal returns %d", r
);
247 increds
.client
= client_principal
;
248 increds
.times
.endtime
= 0;
249 /* Ask for DES since that is what V4 understands */
250 get_creds_enctype((&increds
)) = ENCTYPE_DES_CBC_CRC
;
252 r
= krb5_get_credentials(context
, 0, _krb425_ccache
, &increds
, creds
);
259 get_user_realm(krb5_context context
, char *realm
)
261 static krb5_principal client_principal
= 0;
265 krb5_cc_default(context
, &_krb425_ccache
);
266 if (!client_principal
)
267 krb5_cc_get_principal(context
, _krb425_ccache
, &client_principal
);
269 i
= realm_len(context
, client_principal
);
270 if (i
> REALM_SZ
-1) i
= REALM_SZ
-1;
271 strncpy(realm
,realm_data(context
, client_principal
), i
);
278 aklog_chpass(char *userName
, char *oldPasswd
, char *newPasswd
, char **message
)
284 aklog_passwdexpired(char *userName
, char **message
)
290 aklog_passwdrestrictions(char *userName
, char *newPasswd
, char *oldPasswd
,
297 aklog_getpasswd(char * userName
)
304 aklog_open(char *name
, char *domain
, int mode
, char *options
)
308 /* I can't find this documented anywhere, but it looks like the "options"
309 * string is NUL-delimited (so NULs replace commas in the configuration
310 * file), and the end of the list is marked by an extra NUL. */
312 for (opt
= options
; opt
&& *opt
; opt
+= strlen(opt
) + 1) {
313 if (strcasecmp(opt
, "uidpag") == 0) {
316 if (strcasecmp(opt
, "localuid") == 0) {
324 get_cellconfig(char *cell
, struct afsconf_cell
*cellconfig
, char *local_cell
, char *linkedcell
)
327 struct afsconf_dir
*configdir
;
329 memset(local_cell
, 0, sizeof(local_cell
));
330 memset(cellconfig
, 0, sizeof(*cellconfig
));
332 if (!(configdir
= afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH
))) {
336 if (afsconf_GetLocalCell(configdir
, local_cell
, MAXCELLCHARS
)) {
337 return AFSCONF_FAILURE
;
340 if ((cell
== NULL
) || (cell
[0] == 0))
343 linkedcell
[0] = '\0';
344 if (afsconf_GetCellInfo(configdir
, cell
, NULL
, cellconfig
)) {
345 status
= AFSCONF_NOTFOUND
;
347 if (cellconfig
->linkedCell
)
348 strncpy(linkedcell
,cellconfig
->linkedCell
,MAXCELLCHARS
);
350 (void) afsconf_Close(configdir
);
356 * Log to a cell. If the cell has already been logged to, return without
357 * doing anything. Otherwise, log to it and mark that it has been logged
361 auth_to_cell(krb5_context context
, char *user
, char *cell
, char *realm
)
364 char username
[BUFSIZ
]; /* To hold client username structure */
365 afs_int32 viceId
; /* AFS uid of user */
367 char name
[ANAME_SZ
]; /* Name of afs key */
368 char primary_instance
[INST_SZ
]; /* Instance of afs key */
369 char secondary_instance
[INST_SZ
]; /* Backup instance to try */
370 int try_secondary
= 0; /* Flag to indicate if we try second */
371 char realm_of_user
[REALM_SZ
]; /* Kerberos realm of user */
372 char realm_of_cell
[REALM_SZ
]; /* Kerberos realm of cell */
373 char local_cell
[MAXCELLCHARS
+1];
374 char cell_to_use
[MAXCELLCHARS
+1]; /* Cell to authenticate to */
375 static char confname
[512] = { 0 };
376 krb5_creds
*v5cred
= NULL
;
377 struct ktc_principal aserver
;
378 int afssetpag
= 0, uid
= -1;
379 struct passwd
*pwd
= NULL
;
380 struct ktc_setTokenData
*token
= NULL
;
381 struct ktc_tokenUnion
*rxkadToken
= NULL
;
383 memset(name
, 0, sizeof(name
));
384 memset(primary_instance
, 0, sizeof(primary_instance
));
385 memset(secondary_instance
, 0, sizeof(secondary_instance
));
386 memset(realm_of_user
, 0, sizeof(realm_of_user
));
387 memset(realm_of_cell
, 0, sizeof(realm_of_cell
));
388 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog starting: user %s uid %d", user
, getuid());
389 if (confname
[0] == '\0') {
390 strncpy(confname
, AFSDIR_CLIENT_ETC_DIRPATH
, sizeof(confname
));
391 confname
[sizeof(confname
) - 2] = '\0';
394 /* NULL or empty cell returns information on local cell */
395 if ((status
= get_cellconfig(cell
, &ak_cellconfig
,
396 local_cell
, linkedcell
))) {
397 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: get_cellconfig returns %d", status
);
401 strncpy(cell_to_use
, ak_cellconfig
.name
, MAXCELLCHARS
);
402 cell_to_use
[MAXCELLCHARS
] = 0;
405 * Find out which realm we're supposed to authenticate to. If one
406 * is not included, use the kerberos realm found in the credentials
410 if (realm
&& realm
[0]) {
411 strcpy(realm_of_cell
, realm
);
414 char *afs_realm
= afs_realm_of_cell(context
, &ak_cellconfig
, FALSE
);
417 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: afs_realm_of_cell returns %d", status
);
418 return AFSCONF_FAILURE
;
421 strcpy(realm_of_cell
, afs_realm
);
424 /* We use the afs.<cellname> convention here...
426 * Doug Engert's original code had principals of the form:
430 * in the KDC, so the name wouldn't conflict with DFS. Since we're
431 * not using DFS, I changed it just to look for the following
437 * Because people are transitioning from afs@realm to afs/cell,
438 * we configure things so that if the first one isn't found, we
439 * try the second one. You can select which one you prefer with
440 * a configure option.
443 strcpy(name
, AFSKEY
);
445 if (1 || strcasecmp(cell_to_use
, realm_of_cell
) != 0) {
446 strncpy(primary_instance
, cell_to_use
, sizeof(primary_instance
));
447 primary_instance
[sizeof(primary_instance
)-1] = '\0';
448 if (strcasecmp(cell_to_use
, realm_of_cell
) == 0) {
450 secondary_instance
[0] = '\0';
453 primary_instance
[0] = '\0';
455 strncpy(secondary_instance
, cell_to_use
,
456 sizeof(secondary_instance
));
457 secondary_instance
[sizeof(secondary_instance
)-1] = '\0';
460 token
= token_buildTokenJar(ak_cellconfig
.name
);
462 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: token_buildTokenJar returns NULL");
467 * Extract the session key from the ticket file and hand-frob an
468 * afs style authenticator.
472 * Try to obtain AFS tickets. Because there are two valid service
473 * names, we will try both, but trying the more specific first.
475 * afs/<cell>@<realm> i.e. allow for single name with "."
479 status
= get_credv5(context
, user
, name
, primary_instance
, realm_of_cell
,
482 if ((status
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
483 status
== KRB5KRB_ERR_GENERIC
) && !realm_of_cell
[0]) {
484 char *afs_realm
= afs_realm_of_cell(context
, &ak_cellconfig
, TRUE
);
487 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: afs_realm_of_cell returns %d", status
);
488 status
= AFSCONF_FAILURE
;
492 strcpy(realm_of_cell
, afs_realm
);
494 if (strcasecmp(cell_to_use
, realm_of_cell
) == 0) {
496 secondary_instance
[0] = '\0';
499 status
= get_credv5(context
, user
, name
, primary_instance
,
500 realm_of_cell
, &v5cred
);
502 if (status
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
||
503 status
== KRB5KRB_ERR_GENERIC
) {
505 status
= get_credv5(context
, user
, name
, secondary_instance
,
506 realm_of_cell
, &v5cred
);
510 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: get_credv5 returns %d", status
);
514 strncpy(aserver
.name
, AFSKEY
, MAXKTCNAMELEN
- 1);
515 strncpy(aserver
.instance
, AFSINST
, MAXKTCNAMELEN
- 1);
516 strncpy(aserver
.cell
, cell_to_use
, MAXKTCREALMLEN
- 1);
519 * The default is to use rxkad2b, which means we put in a full
520 * V5 ticket. If the user specifies -524, we talk to the
521 * 524 ticket converter.
527 struct ktc_token atoken
;
529 len
= min(get_princ_len(context
, v5cred
->client
, 0),
530 second_comp(context
, v5cred
->client
) ?
531 MAXKTCNAMELEN
- 2 : MAXKTCNAMELEN
- 1);
532 strncpy(username
, get_princ_str(context
, v5cred
->client
, 0), len
);
533 username
[len
] = '\0';
535 if (second_comp(context
, v5cred
->client
)) {
536 strcat(username
, ".");
537 p
= username
+ strlen(username
);
538 len
= min(get_princ_len(context
, v5cred
->client
, 1),
539 MAXKTCNAMELEN
- strlen(username
) - 1);
540 strncpy(p
, get_princ_str(context
, v5cred
->client
, 1), len
);
544 memset(&atoken
, 0, sizeof(atoken
));
545 atoken
.kvno
= RXKAD_TKT_TYPE_KERBEROS_V5
;
546 atoken
.startTime
= v5cred
->times
.starttime
;;
547 atoken
.endTime
= v5cred
->times
.endtime
;
548 memcpy(&atoken
.sessionKey
, get_cred_keydata(v5cred
),
549 get_cred_keylen(v5cred
));
550 atoken
.ticketLen
= v5cred
->ticket
.length
;
551 memcpy(atoken
.ticket
, v5cred
->ticket
.data
, atoken
.ticketLen
);
553 status
= token_importRxkadViceId(&rxkadToken
, &atoken
, 0);
555 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: importRxkad failed with %d", status
);
560 status
= token_addToken(token
, rxkadToken
);
562 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: addToken failed with %d", status
);
566 if ((status
= get_user_realm(context
, realm_of_user
))) {
567 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: get_user_realm returns %d", status
);
568 status
= KRB5_REALM_UNKNOWN
;
571 if (strcmp(realm_of_user
, realm_of_cell
)) {
572 strcat(username
, "@");
573 strcat(username
, realm_of_user
);
578 pwd
= getpwnam(user
);
580 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: getpwnam %s failed", user
);
581 status
= AUTH_FAILURE
;
586 viceId
= ANONYMOUSID
;
589 /* This actually crashes long-running daemons */
590 if (!pr_Initialize (0, confname
, aserver
.cell
))
591 status
= pr_SNameToId (username
, &viceId
);
593 viceId
= ANONYMOUSID
;
596 * This actually only works assuming that your uid and pts space match
597 * and probably this works only for the local cell anyway.
601 viceId
= pwd
->pw_uid
;
607 /* Don't do first-time registration. Handle only the simple case */
608 if ((status
== 0) && (viceId
!= ANONYMOUSID
)) {
609 status
= token_setRxkadViceId(rxkadToken
, viceId
);
611 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: Error %d setting rxkad ViceId", status
);
614 token_replaceToken(token
, &rxkadToken
);
615 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog: setting tokens for ViceId %d\n",
620 #ifndef AFS_AIX51_ENV
621 /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before
622 * this routine, it will not add the token. It is not clear what
623 * is going on here! So we will do the following operation.
624 * On AIX 5 this kills our parent. So we won't.
626 write(2,"",0); /* dummy write */
629 /* If uidpag is set, we want to use UID-based PAGs, and not real PAGs, so
630 * don't create a new PAG if we're not in a PAG. But if uidpag is _not_
631 * set, just always create a new PAG. */
633 afssetpag
= (getpagvalue("afs") > 0) ? 1 : 0;
635 /* we can't set a UID PAG if we're already in a real PAG. Whine */
636 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: uidpag is set, but we are "
637 "already in a PAG; this cannot work! "
638 "Attempting to continue by creating a "
644 token_setPag(token
, afssetpag
);
646 if (uid
== 0 && uidpag
) {
647 /* We are root, and we want to use UID-based PAGs. So, fork a child
648 * and setuid before setting tokens, so we set the tokens for the
650 struct sigaction newAction
, origAction
;
654 sigemptyset(&newAction
.sa_mask
);
655 newAction
.sa_handler
= SIG_DFL
;
656 newAction
.sa_flags
= 0;
657 status
= sigaction(SIGCHLD
, &newAction
, &origAction
);
659 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: sigaction returned %d", status
);
660 status
= AUTH_FAILURE
;
663 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog: in daemon? forking to set tokens");
666 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog child: setting tokens");
668 status
= ktc_SetTokenEx(token
);
670 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog child: set tokens, returning %d", status
);
671 exit((status
== 0)?0:255);
674 pcid
= waitpid(cid
, &wstatus
, 0);
675 } while ((pcid
== -1) && (errno
== EINTR
));
676 if ((pcid
== cid
) && WIFEXITED(wstatus
))
677 status
= WEXITSTATUS(wstatus
);
681 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog: collected child status %d", status
);
682 sigaction(SIGCHLD
, &origAction
, NULL
);
684 status
= ktc_SetTokenEx(token
);
687 syslog(LOG_AUTH
|LOG_ERR
, "LAM aklog: set tokens returned %d", status
);
689 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog: set tokens, pag %d", getpagvalue("afs"));
693 token_freeToken(&rxkadToken
);
695 token_FreeSet(&token
);
700 aklog_initialize(struct secmethod_table
*meths
)
702 memset(meths
, 0, sizeof(struct secmethod_table
));
703 syslog(LOG_AUTH
|LOG_DEBUG
, "LAM aklog loaded: uid %d pag %d", getuid(), getpagvalue("afs"));
705 * Initialize the exported interface routines.
706 * Aside from method_authenticate and method_open, these are just no-ops.
708 meths
->method_chpass
= aklog_chpass
;
709 meths
->method_authenticate
= aklog_authenticate
;
710 meths
->method_passwdexpired
= aklog_passwdexpired
;
711 meths
->method_passwdrestrictions
= aklog_passwdrestrictions
;
712 meths
->method_getpasswd
= aklog_getpasswd
;
713 meths
->method_open
= aklog_open
;
717 #endif /* AFS_AIX51_ENV */