dce stuff, by way of From Ake Sandgren <ake@cs.umu.se>

git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@8506 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
Assar Westerlund
2000-07-01 19:19:31 +00:00
parent 3c72c627d3
commit 408afcd320
7 changed files with 1489 additions and 0 deletions

22
appl/dceutils/Makefile.am Normal file
View File

@@ -0,0 +1,22 @@
# $Id$
include $(top_srcdir)/Makefile.am.common
DFSPROGS = k5dcecon
if AIX
AIX_DFSPROGS = dpagaix
dpagaix_LDFLAGS = -Wl,-edpagaix -Wl,dfspag.exp
dpagaix_LDADD =
CC = xlc_r4
endif
libexec_PROGRAMS = $(DFSPROGS) $(AIX_DFSPROGS)
LIB_dce = -ldce
k5dcecon_SOURCES = k5dcecon.c k5dce.h
dpagaix_SOURCES = dpagaix.c
LDADD = $(LIB_roken) $(LIB_dce)

View File

@@ -0,0 +1,335 @@
KERBEROS and DCE INTEROPERABILITY ROUTINES
WHAT'S NEW
When k5dcecon was examining the ticket caches looking to
update one with a newer TGT, it might update the wrong
one for the correct user. This problem was reported by PNNL,
and is now fixed.
Any Kerberized application can now use a forwarded TGT to establish a
DCE context, or can use a previously established DCE context. This is
both a functional improvement and a performance improvement.
BACKGROUND
The MIT Kerberos 5 Release 1.x and DCE 1.1 can interoperate in a
number of ways. This is possible because:
o DCE used Kerberos 5 internally. Based on the MIT code as of beta 4
or so, with additional changes.
o The DCE security server can act as a K5 KDC, as defined in RFC 1510
and responds on port 88.
o On the clients, DCE and Kerberos use the same format for the ticket
cache, and then can share it. The KRB5CCNAME environment variable points
at the cache.
o On the clients, DCE and Kerberos use the same format for the srvtab
file. DCE refers to is a /krb5/v5srvtab and Kerberos as
/etc/krb5.keytab. They can be symlinked.
o MIT has added many options to the krb5.conf configuration file
which allows newer features of Release 1.0 to be turned off to match
the earlier version of Kerberos upon which DCE is based.
o DCE will accept a externally obtained Kerberos TGT in place of a
password when establishing a DCE context.
There are some areas where they differ, including the following:
o Administration of the database and the keytab files is done by the
DCE routines, rather the the Kerberos kadmin.
o User password changes must be done using the DCE commands. Kpasswd
does not work. (But there are mods to Kerberos to use the v5passwd
with DCE.
o DCE goes beyond authentication only, and provides authorization via
the PAC, and the dce-ptgt tickets stored in the cache. Thus a
Kerberos KDC can not act as a DCE security server.
o A DCE cell and Kerberos realm can cross-realm authenticate, but
there can be no intermediate realms. (There are other problems
in this area as well. But directly connected realms/cells do work.)
o You can't link a module with the DCE library and the Kerberos
library. They have conflicting routines, static data and structures.
One of the main features of DCE is the Distributed File System
DFS. Access to DFS requires authentication and authorization, and when
one uses a Kerberized network utility such as telnet, a forwarded
Kerberos ticket can be used to establish the DCE context to allow
access to DFS.
NEW TO THIS RELEASE
This release introduces sharing of a DCE context, and PAG, and allows
any Kerberized application to establish or share the context. This is
made possible by using an undocumented feature of DCE which is on at
least the Transarc and IBM releases of DCE 1.1.
I am in the process of trying to get this contributed to the general
DCE 1.2.2 release as a patch, so it could be included in other vendors
products. HP has expressed interest in doing this, as well as the
OpenGroup if the modification is contributed. You can help by
requesting Transarc and/or IBM to submit this modification to the
OpenGroup and ask your vendor to adopt this modification.
The feature is a modification to the setpag() system call which will
allow an authorized process to set the PAG to a specific value, and
thus allow unrelated processes to share the same PAG.
This then allows the Kerberized daemons such as kshd, to exec a DCE
module which established the DCE context. Kshd then sets the
KRB5CCNAME environment variable and then issues the setpag() to use
this context. This solves the linking problem. This is done via the
k5dfspag.c routine.
The k5dfspag.c code is compiled with the lib/krb5/os routines and
included in the libkrb5. A daemon calls krb5_dfs_pag after the
krb5_kuserok has determined that the Kerberos principal and local
userid pair are acceptable. This should be done early so as to give
the daemon access to the home directory which may be located on DFS.
If the .k5login file is used by krb5_kuserok it will need to be
accessed by the daemon and will need special ACL handling.
The krb5_dfs_pag routine will exec the k5dcecon module to do all the
real work. Upon return, if a PAG is obtained, krb5_dfs_pag with set
the PAG for the current process to the returned PAG value. It will
also set the KRB5CCNAME environment as well. Under DCE the PAG value
is the nnnnnnn part of the name of the cache:
FILE:/opt/dcelocal/var/security/creds/dcecred_nnnnnnnn.
The k5dcecon routine will attempt to use TGT which may have been
forwarded, to convert it to a DCE context. If there is no TGT, an
attempt will be made to join an existing PAG for the local userid, and
Kerberos principal. If there are existing PAGs, and a forwarded TGT,
k5dcecon will check the lifetime of the forwarded TGT, and if it is
less then the lifetime of the PAG, it will just join the PAG. If it
is greater, it will refresh the PAG using the forwarded TGT.
This approach has the advantage of not requiring many new tickets from
having to be obtained, and allows one to refresh a DCE context, or use
an already established context.
If the system also has AFS, the AFS krb5_afs_pag should be called
after the krb5_dfs_pag, since cache pointed at via the KRB5CCNAME may
have changed, such as if a DFS PAG has been joined. The AFS code does
not have the capability to join an existing AFS PAG, but can use the
same cache which might already had a
afsx/<afs.cell.name>@<k5.realm.name> service ticket.
WHAT'S IN THIS RELEASE
The k5prelogin, k5dcelogin, k5afslogin (with ak5log) were designed to
be slipped in between telnetd or klogind and login.krb5. They would
use a forwarded Kerberos ticket to establish a DCE context. They are
the older programs which are included here. They work on all DCE
platforms, and don't take advantage of the undocumented setpag
feature. (A version of k5dcelogin is being included with DCE 1.2.2)
K5dcecon is the new program which can be used to create, update or
join a DCE context. k5dcecon returns KRB5CCNAME string which contains
the PAG.
k5dfspag.c is to be built in the MIT Kerberos 5 release 1.0 patchlevel
1 and added to the libkrb5. It will exec k5dcecon and upon return set
the KRB5CCNAME and PAG. Mods to Kerberized klogind, rshd, telnetd,
ftpd are available to use the k5dfspag.
Testpag.c is a test programs to see if the PAG can be set.
The cpwkey.c routine can be used to change a key in the DCE registry,
by adding the key directly, or by setting the salt/pepper and password
or by providing the key and the pepper. This could be useful when
coping keys from a K4 or AFS database to DCE. It can also be used when
setting a DCE to K5 cross-cell key. This program is a test program
For mass inserts, it should be rewritten to read from stdin.
K5dcelogin can also be called directly, much like dce_login.
I use the following commands in effect do the same thing as dce_login
and get a forwardable ticket, DCE context and an AFS token:
#!/bin/csh
# simulate a dce_login using krb5 kinit and k5dcelogin
#
setenv KRB5CCNAME FILE:/tmp/krb5cc_p$$
/krb5/bin/kinit -f
exec /krb5/sbin/k5dcelogin /krb5/sbin/k5afslogin /bin/csh
#exec /krb5/sbin/k5dcelogin /bin/csh
This could be useful in a mixed cell where "AS_REQ" messages are
handled by a K5 KDC, but DCE RPCs are handled by the DCE security
server.
TESTING THE SETPAG
The krb5_dfs_pag routine relies on an undocumented feature which is
in the AIX and Transarc Solaris ports of DCE and has been recently
added to the SGI version. To test if this feature is present
on some other DFS implementation use the testpag routine.
The testpag routine attempts to set a PAG value to one you supply. It
uses the afs_syscall with the afs_setpag, and passes the supplied
PAG value as the next parameter. On an unmodifed system, this
will be ignored, and a new will be set. You should also check that
if run as a user, you cannot join a PAG owned by another user.
When run as root, any PAG should be usable.
On a machine with DFS running, do a dce_login to get a DCE context and
PAG. ECHO the KRB5CCNAME and look at the nnnnnnnn at the end. It
should look like an 8 char hex value, which may be 41ffxxxx on some
systems.
Su to root and unsetenv KRB5CCNAME. Do a testpag -n nnnnnnnn where
nnnnnnnn is the PAG obtained for the above name.
It should look like this example on an AIX 4.1.4 system:
pembroke# ./testpag -n 63dc9997
calling k5dcepag newpag=63dc9997
PAG returned = 63dc9997
You will be running under a new shell with the PAG and KRB5CCNAME set.
If the PAG returned is the same as the newpag, then it worked. You can
further verify this by doing a DCE klist, cd to DFS and a DCE klist
again. The klist should show some tickets for DFS servers.
If the PAG returned is not the same, and repeated attempts show a
returned PAG decremented by 1 from the previous returned PAG, then
this system does not have the modification For example:
# ./testpag -n 41fffff9
calling k5dcepag newpag=41fffff9
PAG returned = 41fffff8
# ./testpag -n 41fffff9
calling k5dcepag newpag=41fffff9
PAG returned = 41fffff7
In this case the syscall is ignoring the newpag parameter.
Running it with -n 0 should get the next PAG value with or without
this modification.
If the DFS kernel extensions are not installed, you would get
something like this:
caliban.ctd.anl.gov% ./testpag -n 012345678
calling k5dcepag newpag=012345678
Setpag failed with a system error
PAG returned = ffffffff
Not a good pag value
If you DFS implementation does not have this modification, you could
attempt to install it yourself. But this requires source and requires
modifications to the kernel extensions. At the end of this note is an
untested sample using the DCE 1.2.2 source code. You can also contact
your system vendor and ask for this modification.
UNICOS has a similar function setppag(newpag) which can be used to set
the PAG of the parent. Contact me if you are interested.
HOW TO INSTALL
Examine the k5dfspag.c file to make sure the DFS syscalls are correct
for your platform. See the /opt/dcelocal/share/include/dcedfs/syscall.h
on Solaris for example.
You should build the testpag routine and make sure it works before
adding all the other mods. If it fails you can still use the klogind
and telnetd with the k5prelogin and k5dcelogin code.
If you intend to install with a prefix other then /krb5, change:
DPAGAIX and K5DCECON in k5dfspag.c; the three references in
k5prelogin.c; and the DESTDIR in the Makefile.
Get k5101.cdiff.xxxxxx.tar file and install the mods for ANL_DFS_PAG
and ANL_DCE to the MIT Kerberos 5 source. These mods turn on some DCE
related changes and the calls to krb5_dfs_pag.
Symlink or copy the k5dfspag.c to the src/lib/krb5/os directory.
Add the -DANL_DFS_PAG and -DANL_DCE flags to the configuration.
Configure and Build the Kerberos v5.
Modify the k5dce Makefile for your system.
Build the k5dcecon and related programs.
Install both the MIT Kerberos v5 and the k5dcecon and dpagaix if AIX.
The makefile can also build k5dcelogin and k5prelogin. The install
can install k5dcelogin, k5prelogin and update the links for login.krb5
-> k5prelogin and moving login.krb5 to login.k5. If you will be using
the k5dcecon/k5dfspag with the Kerberos mods, you don't need
k5prelogin, or the links changed, and may not need k5dcelogin.
Note that Transarc has obfuscated the entries to the lib, and
the 1.0.3a is different from the 1.1. You may need to build two
versions of the k5dcelogin and/or k5dcecon one for each.
AIX ONLY
The dpagaix routine is needed for AIX because of the way they do the
syscalls.
The following fix.aix.libdce.mk is not needed if dce 2.1.0.21
has been installed. This PTF exposed the needed entrypoints.
The fix.aix.libdce.mk is a Makefile for AIX 4.x to add the required
external entry points to the libdce.a. These are needed by k5dcecon
and k5dcelogin. A bug report was submitted to IBM on this, and it was
rejected. But since DCE 1.2.2 will have a k5dcelogin, this should not
be needed with 1.2.2
Copy /usr/lib/libdce.a to /usr/libdce.a.orig before starting. Copy the
makefile to its own directory. It will create a new libdce.a which you
need to copy back to /usr/lib/libdce.a You will need to reboot the
machine. See the /usr/lpp/dce/examples/inst/README.AIX for a similar
procedure. IBM was not responsive in a request to have these added.
UNTESTED KERNEL EXTENSION FOR SETPAG
*** src/file/osi/,osi_pag.c Wed Oct 2 13:03:05 1996
--- src/file/osi/osi_pag.c Mon Jul 28 13:53:13 1997
***************
*** 293,298 ****
--- 293,302 ----
int code;
osi_MakePreemptionRight();
+ /* allow sharing of a PAG by non child processes DEE- 6/6/97 */
+ if (unused && osi_GetUID(osi_getucred()) == 0) {
+ newpag = unused;
+ } else {
osi_mutex_enter(&osi_pagLock);
now = osi_Time();
soonest = osi_firstPagTime +
***************
*** 309,314 ****
--- 313,319 ----
}
osi_mutex_exit(&osi_pagLock);
newpag = osi_genpag();
+ }
osi_pcred_lock(p);
credp = crcopy(osi_getucred());
code = osi_SetPagInCred(credp, newpag);
Created 07/08/96
Modified 09/30/96
Modified 11/19/96
Modified 12/19/96
Modified 06/20/97
Modified 07/28/97
Modified 02/18/98
Douglas E. Engert <DEEngert@anl.gov>
Argonne National Laboratory
9700 South Cass Avenue
Argonne, Illinois 60439
(630) 252-5444

3
appl/dceutils/dfspag.exp Normal file
View File

@@ -0,0 +1,3 @@
#!/unix
* kernel extentions used to get the pag
kafs_syscall syscall

23
appl/dceutils/dpagaix.c Normal file
View File

@@ -0,0 +1,23 @@
/*
* dpagaix.c
* On AIX we need to get the kernel extentions
* with the DFS kafs_syscall in it.
* We might be running on a system
* where DFS is not active.
* So we use this dummy routine which
* might not load to do the dirty work
*
* DCE does this with the /usr/lib/drivers/dfsloadobj
*
*/
int dpagaix(parm1, parm2, parm3, parm4, parm5, parm6)
int parm1;
int parm2;
int parm3;
int parm4;
int parm5;
int parm6;
{
return(kafs_syscall(parm1, parm2, parm3, parm4, parm5, parm6));
}

165
appl/dceutils/k5dce.h Normal file
View File

@@ -0,0 +1,165 @@
/* dummy K5 routines which are needed to get this to
* compile without having access ti the DCE versions
* of the header files.
* Thiis is very crude, and OSF needs to expose the K5
* API.
*/
#ifdef sun
/* Transarc obfascates these routines */
#ifdef DCE_1_1
#define krb5_init_ets _dce_PkjKqOaklP
#define krb5_copy_creds _dce_LuFxPiITzD
#define krb5_unparse_name _dce_LWHtAuNgRV
#define krb5_get_default_realm _dce_vDruhprWGh
#define krb5_build_principal _dce_qwAalSzTtF
#define krb5_build_principal_ext _dce_vhafIQlejW
#define krb5_build_principal_va _dce_alsqToMmuJ
#define krb5_cc_default _dce_KZRshhTXhE
#define krb5_cc_default_name _dce_bzJVAjHXVQ
#define sec_login_krb5_add_cred _dce_ePDtOJTZvU
#else /* DCE 1.0.3a */
#define krb5_init_ets _dce_BmLRpOVsBo
#define krb5_copy_creds _dce_VGwSEBNwaf
#define krb5_unparse_name _dce_PgAOkJoMXA
#define krb5_get_default_realm _dce_plVOzStKyK
#define krb5_build_principal _dce_uAKSsluIFy
#define krb5_build_principal_ext _dce_tRMpPiRada
#define krb5_build_principal_va _dce_SxnLejZemH
#define krb5_cc_default _dce_SeKosWFnsv
#define krb5_cc_default_name _dce_qJeaphJWVc
#define sec_login_krb5_add_cred _dce_uHwRasumsN
#endif
#endif
/* Define the bare minimum k5 structures which are needed
* by this program. Since the krb5 includes are not supplied
* with DCE, these were based on the MIT Kerberos 5 beta 3
* which should match the DCE as of 1.0.3 at least.
* The tricky one is the krb5_creds, since one is allocated
* by this program, and it needs access to the client principal
* in it.
* Note that there are no function prototypes, so there is no
* compile time checking.
* DEE 07/11/95
*/
#define NPROTOTYPE(x) ()
typedef int krb5_int32; /* assuming all DCE systems are 32 bit */
typedef short krb5short; /* assuming short is 16 bit */
typedef krb5_int32 krb5_error_code;
typedef unsigned char krb5_octet;
typedef krb5_octet krb5_boolean;
typedef krb5short krb5_keytype; /* in k5.2 it's a short */
typedef krb5_int32 krb5_flags;
typedef krb5_int32 krb5_timestamp;
typedef char * krb5_pointer; /* pointer to unexposed data */
typedef struct _krb5_ccache {
struct _krb5_cc_ops *ops;
krb5_pointer data;
} *krb5_ccache;
typedef struct _krb5_cc_ops {
char *prefix;
char *(*get_name) NPROTOTYPE((krb5_ccache));
krb5_error_code (*resolve) NPROTOTYPE((krb5_ccache *, char *));
krb5_error_code (*gen_new) NPROTOTYPE((krb5_ccache *));
krb5_error_code (*init) NPROTOTYPE((krb5_ccache, krb5_principal));
krb5_error_code (*destroy) NPROTOTYPE((krb5_ccache));
krb5_error_code (*close) NPROTOTYPE((krb5_ccache));
krb5_error_code (*store) NPROTOTYPE((krb5_ccache, krb5_creds *));
krb5_error_code (*retrieve) NPROTOTYPE((krb5_ccache, krb5_flags,
krb5_creds *, krb5_creds *));
krb5_error_code (*get_princ) NPROTOTYPE((krb5_ccache,
krb5_principal *));
krb5_error_code (*get_first) NPROTOTYPE((krb5_ccache,
krb5_cc_cursor *));
krb5_error_code (*get_next) NPROTOTYPE((krb5_ccache, krb5_cc_cursor *,
krb5_creds *));
krb5_error_code (*end_get) NPROTOTYPE((krb5_ccache, krb5_cc_cursor *));
krb5_error_code (*remove_cred) NPROTOTYPE((krb5_ccache, krb5_flags,
krb5_creds *));
krb5_error_code (*set_flags) NPROTOTYPE((krb5_ccache, krb5_flags));
} krb5_cc_ops;
typedef struct _krb5_keyblock {
krb5_keytype keytype;
int length;
krb5_octet *contents;
} krb5_keyblock;
typedef struct _krb5_ticket_times {
krb5_timestamp authtime;
krb5_timestamp starttime;
krb5_timestamp endtime;
krb5_timestamp renew_till;
} krb5_ticket_times;
typedef krb5_pointer krb5_cc_cursor;
typedef struct _krb5_data {
int length;
char *data;
} krb5_data;
typedef struct _krb5_authdata {
int ad_type;
int length;
krb5_octet *contents;
} krb5_authdata;
typedef struct _krb5_creds {
krb5_pointer client;
krb5_pointer server;
krb5_keyblock keyblock;
krb5_ticket_times times;
krb5_boolean is_skey;
krb5_flags ticket_flags;
krb5_pointer **addresses;
krb5_data ticket;
krb5_data second_ticket;
krb5_pointer **authdata;
} krb5_creds;
typedef krb5_pointer krb5_principal;
#define KRB5_CC_END 336760974
#define KRB5_TC_OPENCLOSE 0x00000001
/* Ticket flags */
/* flags are 32 bits; each host is responsible to put the 4 bytes
representing these bits into net order before transmission */
/* #define TKT_FLG_RESERVED 0x80000000 */
#define TKT_FLG_FORWARDABLE 0x40000000
#define TKT_FLG_FORWARDED 0x20000000
#define TKT_FLG_PROXIABLE 0x10000000
#define TKT_FLG_PROXY 0x08000000
#define TKT_FLG_MAY_POSTDATE 0x04000000
#define TKT_FLG_POSTDATED 0x02000000
#define TKT_FLG_INVALID 0x01000000
#define TKT_FLG_RENEWABLE 0x00800000
#define TKT_FLG_INITIAL 0x00400000
#define TKT_FLG_PRE_AUTH 0x00200000
#define TKT_FLG_HW_AUTH 0x00100000
#ifdef PK_INIT
#define TKT_FLG_PUBKEY_PREAUTH 0x00080000
#define TKT_FLG_DIGSIGN_PREAUTH 0x00040000
#define TKT_FLG_PRIVKEY_PREAUTH 0x00020000
#endif
#define krb5_cc_get_principal(cache, principal) (*(cache)->ops->get_princ)(cache, principal)
#define krb5_cc_set_flags(cache, flags) (*(cache)->ops->set_flags)(cache, flags)
#define krb5_cc_get_name(cache) (*(cache)->ops->get_name)(cache)
#define krb5_cc_start_seq_get(cache, cursor) (*(cache)->ops->get_first)(cache, cursor)
#define krb5_cc_next_cred(cache, cursor, creds) (*(cache)->ops->get_next)(cache, cursor, creds)
#define krb5_cc_destroy(cache) (*(cache)->ops->destroy)(cache)
#define krb5_cc_end_seq_get(cache, cursor) (*(cache)->ops->end_get)(cache, cursor)
/* end of k5 dummy typedefs */

791
appl/dceutils/k5dcecon.c Normal file
View File

@@ -0,0 +1,791 @@
/*
* (c) Copyright 1995 HEWLETT-PACKARD COMPANY
*
* To anyone who acknowledges that this file is provided
* "AS IS" without any express or implied warranty:
* permission to use, copy, modify, and distribute this
* file for any purpose is hereby granted without fee,
* provided that the above copyright notice and this
* notice appears in all copies, and that the name of
* Hewlett-Packard Company not be used in advertising or
* publicity pertaining to distribution of the software
* without specific, written prior permission. Hewlett-
* Packard Company makes no representations about the
* suitability of this software for any purpose.
*
*/
/*
* k5dcecon - Program to convert a K5 TGT to a DCE context,
* for use with DFS and its PAG.
*
* The program is designed to be called as a sub process,
* and return via stdout the name of the cache which implies
* the PAG which should be used. This program itself does not
* use the cache or PAG itself, so the PAG in the kernel for
* this program may not be set.
*
* The calling program can then use the name of the cache
* to set the KRB5CCNAME and PAG for its self and its children.
*
* If no ticket was passed, an attemplt to join an existing
* PAG will be made.
*
* If a forwarded K5 TGT is passed in, either a new DCE
* context will be created, or an existing one will be updated.
* If the same ticket was already used to create an existing
* context, it will be joined instead.
*
* Parts of this program are based on k5dceauth,c which was
* given to me by HP and by the k5dcelogin.c which I developed.
* A slightly different version of k5dcelogin.c, was added to
* DCE 1.2.2
*
* D. E. Engert 6/17/97 ANL
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <locale.h>
#include <pwd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "k5dce.h"
#include <dce/sec_login.h>
#include <dce/dce_error.h>
#include <dce/passwd.h>
/* #define DEBUG */
#if defined(DEBUG)
#define DEEDEBUG(A) fprintf(stderr,A); fflush(stderr)
#define DEEDEBUG2(A,B) fprintf(stderr,A,B); fflush(stderr)
#else
#define DEEDEBUG(A)
#define DEEDEBUG2(A,B)
#endif
#ifdef __hpux
#define seteuid(A) setresuid(-1,A,-1);
#endif
int k5dcecreate (uid_t, char *, char*, krb5_creds **);
int k5dcecon (uid_t, char *, char *);
int k5dcegettgt (krb5_ccache *, char *, char *, krb5_creds **);
int k5dcematch (uid_t, char *, char *, off_t *, krb5_creds **);
int k5dcesession (uid_t, char *, krb5_creds **, int *,krb5_flags);
char *progname = "k5dcecon";
static time_t now;
#ifdef notdef
#ifdef _AIX
/*---------------------------------------------*/
/* AIX with DCE 1.1 does not have the com_err in the libdce.a
* do a half hearted job of substituting for it.
*/
void com_err(char *p1, int code, ...)
{
int lst;
dce_error_string_t err_string;
dce_error_inq_text(code, err_string, &lst);
fprintf(stderr,"Error %d in %s: %s\n", code, p1, err_string );
}
/*---------------------------------------------*/
void krb5_init_ets()
{
}
#endif
#endif
/*------------------------------------------------*/
/* find a cache to use for our new pag */
/* Since there is no simple way to determine which
* caches are associated with a pag, we will have
* do look around and see what makes most sense on
* different systems.
* on a Solaris system, and in the DCE source,
* the pags always start with a 41.
* this is not true on the IBM, where there does not
* appear to be any pattern.
*
* But since we are always certifing our creds when
* they are received, we can us that fact, and look
* at the first word of the associated data file
* to see that it has a "5". If not don't use.
*/
int k5dcesession(luid, pname, tgt, ppag, tflags)
uid_t luid;
char *pname;
krb5_creds **tgt;
int *ppag;
krb5_flags tflags;
{
DIR *dirp;
struct dirent *direntp;
off_t size;
krb5_timestamp endtime;
int better = 0;
krb5_creds *xtgt;
char prev_name[17] = "";
krb5_timestamp prev_endtime;
off_t prev_size;
u_long prev_pag = 0;
char ccname[64] = "FILE:/opt/dcelocal/var/security/creds/";
error_status_t st;
sec_login_handle_t lcontext = 0;
dce_error_string_t err_string;
int lst;
DEEDEBUG2("k5dcesession looking for flags %8.8x\n",tflags);
dirp = opendir("/opt/dcelocal/var/security/creds/");
if (dirp == NULL) {
return 1;
}
while ( (direntp = readdir( dirp )) != NULL ) {
/*
* (but root has the ffffffff which we are not interested in)
*/
if (!strncmp(direntp->d_name,"dcecred_",8)
&& (strlen(direntp->d_name) == 16)) {
/* looks like a cache name, lets do the stat, etc */
strcpy(ccname+38,direntp->d_name);
if (!k5dcematch(luid, pname, ccname, &size, &xtgt)) {
/* its one of our caches, see if it is better
* i.e. the endtime is farther, and if the endtimes
* are the same, take the larger, as he who has the
* most tickets wins.
* it must also had the same set of flags at least
* i.e. if the forwarded TGT is forwardable, this one must
* be as well.
*/
DEEDEBUG2("Cache:%s",direntp->d_name);
DEEDEBUG2(" size:%d",size);
DEEDEBUG2(" flags:%8.8x",xtgt->ticket_flags);
DEEDEBUG2(" %s",ctime((time_t *)&xtgt->times.endtime));
if ((xtgt->ticket_flags & tflags) == tflags ) {
if (prev_name[0]) {
if (xtgt->times.endtime > prev_endtime) {
better = 1;
} else if ((xtgt->times.endtime = prev_endtime)
&& (size > prev_size)){
better = 1;
}
} else { /* the first */
if (xtgt->times.endtime >= now) {
better = 1;
}
}
if (better) {
strcpy(prev_name, direntp->d_name);
prev_endtime = xtgt->times.endtime;
prev_size = size;
sscanf(prev_name+8,"%8X",&prev_pag);
*tgt = xtgt;
better = 0;
}
}
}
}
}
(void)closedir( dirp );
if (!prev_name[0])
return 1; /* failed to find one */
DEEDEBUG2("Best: %s\n",prev_name);
if (ppag)
*ppag = prev_pag;
strcpy(ccname+38,prev_name);
setenv("KRB5CCNAME",ccname,1);
return(0);
}
/*----------------------------------------------*/
/* see if this cache is for this this principal */
int k5dcematch(luid, pname, ccname, sizep, tgt)
uid_t luid;
char *pname;
char *ccname;
off_t *sizep; /* size of the file */
krb5_creds **tgt;
{
krb5_ccache cache;
struct stat stbuf;
char ccdata[256];
int fd;
int status;
/* DEEDEBUG2("k5dcematch called: cache=%s\n",ccname+38); */
if (!strncmp(ccname,"FILE:",5)) {
strcpy(ccdata,ccname+5);
strcat(ccdata,".data");
/* DEEDEBUG2("Checking the .data file for %s\n",ccdata); */
if (stat(ccdata, &stbuf))
return(1);
if (stbuf.st_uid != luid)
return(1);
if ((fd = open(ccdata,O_RDONLY)) == -1)
return(1);
if ((read(fd,&status,4)) != 4) {
close(fd);
return(1);
}
/* DEEDEBUG2(".data file status = %d\n", status); */
if (status != 5)
return(1);
if (stat(ccname+5, &stbuf))
return(1);
if (stbuf.st_uid != luid)
return(1);
*sizep = stbuf.st_size;
}
return(k5dcegettgt(&cache, ccname, pname, tgt));
}
/*----------------------------------------*/
/* k5dcegettgt - get the tgt from a cache */
int k5dcegettgt(pcache, ccname, pname, tgt)
krb5_ccache *pcache;
char *ccname;
char *pname;
krb5_creds **tgt;
{
krb5_ccache cache;
krb5_cc_cursor cur;
krb5_creds creds;
int code;
int found = 1;
krb5_principal princ;
char *kusername;
krb5_flags flags;
char *sname, *realm, *tgtname = NULL;
/* Since DCE does not expose much of the Kerberos interface,
* we will have to use what we can. This means setting the
* KRB5CCNAME for each file we want to test
* We will also not worry about freeing extra cache structures
* as this this routine is also not exposed, and this should not
* effect this module.
* We should also free the creds contents, but that is not exposed
* either.
*/
setenv("KRB5CCNAME",ccname,1);
cache = NULL;
*tgt = NULL;
if (code = krb5_cc_default(pcache)) {
com_err(progname, code, "while getting ccache");
goto return2;
}
DEEDEBUG("Got cache\n");
flags = 0;
if (code = krb5_cc_set_flags(*pcache, flags)) {
com_err(progname, code,"While setting flags");
goto return2;
}
DEEDEBUG("Set flags\n");
if (code = krb5_cc_get_principal(*pcache, &princ)) {
com_err(progname, code, "While getting princ");
goto return1;
}
DEEDEBUG("Got principal\n");
if (code = krb5_unparse_name(princ, &kusername)) {
com_err(progname, code, "While unparsing principal");
goto return1;
}
DEEDEBUG2("Unparsed to \"%s\"\n", kusername);
DEEDEBUG2("pname is \"%s\"\n", pname);
if (strcmp(kusername, pname)) {
DEEDEBUG("Principals not equal\n");
goto return1;
}
DEEDEBUG("Principals equal\n");
realm = strchr(pname,'@');
realm++;
if ((tgtname = malloc(9 + 2 * strlen(realm))) == 0) {
fprintf(stderr,"Malloc failed for tgtname\n");
goto return1;
}
strcpy(tgtname,"krbtgt/");
strcat(tgtname,realm);
strcat(tgtname,"@");
strcat(tgtname,realm);
DEEDEBUG2("Getting tgt %s\n", tgtname);
if (code = krb5_cc_start_seq_get(*pcache, &cur)) {
com_err(progname, code, "while starting to retrieve tickets");
goto return1;
}
while (!(code = krb5_cc_next_cred(*pcache, &cur, &creds))) {
krb5_creds *cred = &creds;
if (code = krb5_unparse_name(cred->server, &sname)) {
com_err(progname, code, "while unparsing server name");
continue;
}
if (strncmp(sname, tgtname, strlen(tgtname)) == 0) {
DEEDEBUG("FOUND\n");
if (code = krb5_copy_creds(&creds, tgt)) {
com_err(progname, code, "while copying TGT");
goto return1;
}
found = 0;
break;
}
/* we should do a krb5_free_cred_contents(creds); */
}
if (code = krb5_cc_end_seq_get(*pcache, &cur)) {
com_err(progname, code, "while finishing retrieval");
goto return2;
}
return1:
flags = KRB5_TC_OPENCLOSE;
krb5_cc_set_flags(*pcache, flags); /* force a close */
return2:
if (tgtname)
free(tgtname);
return(found);
}
/*------------------------------------------*/
/* Convert a forwarded TGT to a DCE context */
int k5dcecon(luid, luser, pname)
uid_t luid;
char *luser;
char *pname;
{
krb5_creds *ftgt = NULL;
krb5_creds *tgt = NULL;
unsigned32 dfspag;
boolean32 reset_passwd = 0;
int lst;
dce_error_string_t err_string;
char *shell_prog;
krb5_ccache fcache;
char *ccname;
char *kusername;
char *urealm;
char *cp;
int pag;
int code;
krb5_timestamp endtime;
/* If there is no cache to be converted, we should not be here */
if ((ccname = getenv("KRB5CCNAME")) == NULL) {
DEEDEBUG("No KRB5CCNAME\n");
return(1);
}
if (k5dcegettgt(&fcache, ccname, pname, &ftgt)) {
fprintf(stderr, "%s: Did not find TGT\n", progname);
return(1);
}
DEEDEBUG2("flags=%x\n",ftgt->ticket_flags);
if (!(ftgt->ticket_flags & TKT_FLG_FORWARDABLE)){
fprintf(stderr,"Ticket not forwardable\n");
return(0); /* but OK to continue */
}
setenv("KRB5CCNAME","",1);
#define TKT_ACCEPTABLE (TKT_FLG_FORWARDABLE | TKT_FLG_PROXIABLE \
| TKT_FLG_MAY_POSTDATE | TKT_FLG_RENEWABLE | TKT_FLG_HW_AUTH \
| TKT_FLG_PRE_AUTH)
if (!k5dcesession(luid, pname, &tgt, &pag,
(ftgt->ticket_flags & TKT_ACCEPTABLE))) {
if (ftgt->times.endtime > tgt->times.endtime) {
DEEDEBUG("Updating existing cache\n");
return(k5dceupdate(&ftgt, pag));
} else {
DEEDEBUG("Using existing cache\n");
return(0); /* use the original one */
}
}
/* see if the tgts match up */
if ((code = k5dcecreate(luid, luser, pname, &ftgt))) {
return (code);
}
/*
* Destroy the Kerberos5 cred cache file.
* but dont care aout the return code.
*/
DEEDEBUG("Destroying the old cache\n");
if ((code = krb5_cc_destroy(fcache))) {
com_err(progname, code, "while destroying Kerberos5 ccache");
}
return (0);
}
/*--------------------------------------------------*/
/* k5dceupdate - update the cache with a new TGT */
/* Assumed that the KRB5CCNAME has been set */
int k5dceupdate(krbtgt, pag)
krb5_creds **krbtgt;
int pag;
{
krb5_ccache ccache;
int code;
if (code = krb5_cc_default(&ccache)) {
com_err(progname, code, "while opening cache for update");
return(2);
}
if (code = ccache->ops->init(ccache,(*krbtgt)->client)) {
com_err(progname, code, "while reinitilizing cache");
return(3);
}
/* krb5_cc_store_cred */
if (code = ccache->ops->store(ccache, *krbtgt)) {
com_err(progname, code, "while updating cache");
return(2);
}
sec_login_pag_new_tgt(pag, (*krbtgt)->times.endtime);
return(0);
}
/*--------------------------------------------------*/
/* k5dcecreate - create a new DCE context */
int k5dcecreate(luid, luser, pname, krbtgt)
uid_t luid;
char *luser;
char *pname;
krb5_creds **krbtgt;
{
char *cp;
char *urealm;
char *username;
char *defrealm;
uid_t uid;
error_status_t st;
sec_login_handle_t lcontext = 0;
sec_login_auth_src_t auth_src = 0;
boolean32 reset_passwd = 0;
int lst;
dce_error_string_t err_string;
setenv("KRB5CCNAME","",1); /* make sure it not misused */
uid = getuid();
DEEDEBUG2("uid=%d\n",uid);
/* if run as root, change to user, so as to have the
* cache created for the local user even if cross-cell
* If run as a user, let standard file protection work.
*/
if (uid == 0) {
seteuid(luid);
}
cp = strchr(pname,'@');
*cp = '\0';
urealm = ++cp;
DEEDEBUG2("basename=%s\n",cp);
DEEDEBUG2("realm=%s\n",urealm);
/* now build the username as a single string or a /.../cell/user
* if this is a cross cell
*/
if ((username = malloc(7+strlen(pname)+strlen(urealm))) == 0) {
fprintf(stderr,"Malloc failed for username\n");
goto abort;
}
if (krb5_get_default_realm(&defrealm)) {
DEEDEBUG("krb5_get_default_realm failed\n");
goto abort;
}
if (!strcmp(urealm,defrealm)) {
strcpy(username,pname);
} else {
strcpy(username,"/.../");
strcat(username,urealm);
strcat(username,"/");
strcat(username,pname);
}
/*
* Setup a DCE login context
*/
if (sec_login_setup_identity((unsigned_char_p_t)username,
(sec_login_external_tgt|sec_login_proxy_cred),
&lcontext, &st)) {
/*
* Add our TGT.
*/
DEEDEBUG("Adding our new TGT\n");
sec_login_krb5_add_cred(lcontext, *krbtgt, &st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Error while adding credentials for %s because %s\n",
username, err_string);
goto abort;
}
DEEDEBUG("validating and certifying\n");
/*
* Now "validate" and certify the identity,
* usually we would pass a password here, but...
* sec_login_valid_and_cert_ident
* sec_login_validate_identity
*/
if (sec_login_validate_identity(lcontext, 0, &reset_passwd,
&auth_src, &st)) {
DEEDEBUG2("validate_identity st=%d\n",st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr, "Validation error for %s because %s\n",
username, err_string);
goto abort;
}
if (!sec_login_certify_identity(lcontext,&st)) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Credentials not certified because %s\n",err_string);
}
if (reset_passwd) {
fprintf(stderr,
"Password must be changed for %s\n", username);
}
if (auth_src == sec_login_auth_src_local) {
fprintf(stderr,
"Credentials obtained from local registry for %s\n",
username);
}
if (auth_src == sec_login_auth_src_overridden) {
fprintf(stderr, "Validated %s from local override entry, no network credentials obtained\n", username);
goto abort;
}
/*
* Actually create the cred files.
*/
DEEDEBUG("Ceating new cred files.\n");
sec_login_set_context(lcontext, &st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to set context for %s because %s\n",
username, err_string);
goto abort;
}
/*
* Now free up the local context and leave the
* network context with its pag
*/
#if 0
sec_login_release_context(&lcontext, &st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to release context for %s because %s\n",
username, err_string);
goto abort;
}
#endif
}
else {
DEEDEBUG2("validate failed %d\n",st);
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to validate %s because %s\n", username,
err_string);
goto abort;
}
}
else {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to setup login entry for %s because %s\n",
username, err_string);
goto abort;
}
done:
/* if we were root, get back to root */
DEEDEBUG2("sec_login_inq_pag %8.8x\n",
sec_login_inq_pag(lcontext, &st));
if (uid == 0) {
seteuid(0);
}
DEEDEBUG("completed\n");
return(0);
abort:
if (uid == 0) {
seteuid(0);
}
DEEDEBUG("Aborting\n");
return(2);
}
/*-------------------------------------------------*/
main(argc, argv)
int argc;
char *argv[];
{
int status;
extern int optind;
extern char *optarg;
int rv;
char *lusername = NULL;
char *pname = NULL;
int fflag = 0;
struct passwd *pw;
uid_t luid;
uid_t myuid;
char *ccname;
krb5_creds *tgt = NULL;
#ifdef DEBUG
close(2);
open("/tmp/k5dce.debug",O_WRONLY|O_CREAT|O_APPEND);
#endif
if (myuid = getuid()) {
DEEDEBUG2("UID = %d\n",myuid);
exit(33); /* must be root to run this, get out now */
}
while ((rv = getopt(argc,argv,"l:p:fs")) != -1) {
DEEDEBUG2("Arg = %c\n", rv);
switch(rv) {
case 'l': /* user name */
lusername = optarg;
DEEDEBUG2("Optarg = %s\n", optarg);
break;
case 'p': /* principal name */
pname = optarg;
DEEDEBUG2("Optarg = %s\n", optarg);
break;
case 'f': /* convert a forwarded TGT to a context */
fflag++;
break;
case 's': /* old test parameter, ignore it */
break;
}
}
setlocale(LC_ALL, "");
krb5_init_ets();
time(&now); /* set time to check expired tickets */
/* if lusername == NULL, Then user is passed as the USER= variable */
if (!lusername) {
lusername = getenv("USER");
if (!lusername) {
fprintf(stderr, "USER not in environment\n");
return(3);
}
}
if ((pw = getpwnam(lusername)) == NULL) {
fprintf(stderr, "Who are you?\n");
return(44);
}
luid = pw->pw_uid;
if (fflag) {
status = k5dcecon(luid, lusername, pname);
} else {
status = k5dcesession(luid, pname, &tgt, NULL, 0);
}
if (!status) {
printf("%s",getenv("KRB5CCNAME")); /* return via stdout to caller */
DEEDEBUG2("KRB5CCNAME=%s\n",getenv("KRB5CCNAME"));
}
DEEDEBUG2("Returning status %d\n",status);
return (status);
}

150
appl/dceutils/testpag.c Normal file
View File

@@ -0,0 +1,150 @@
/* Test the k5dcepag routine by setting a pag, and
* and execing a shell under this pag.
*
* This allows you to join a PAG which was created
* earlier by some other means.
* for example k5dcecon
*
* Must be run as root for testing only.
*
*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#define POSIX_SETJMP
#define POSIX_SIGNALS
#ifdef POSIX_SIGNALS
typedef struct sigaction handler;
#define handler_init(H,F) (sigemptyset(&(H).sa_mask), \
(H).sa_flags=0, \
(H).sa_handler=(F))
#define handler_swap(S,NEW,OLD) sigaction(S, &NEW, &OLD)
#define handler_set(S,OLD) sigaction(S, &OLD, NULL)
#else
typedef sigtype (*handler)();
#define handler_init(H,F) ((H) = (F))
#define handler_swap(S,NEW,OLD) ((OLD) = signal ((S), (NEW)))
#define handler_set(S,OLD) (signal ((S), (OLD)))
#endif
typedef void sigtype;
/*
* We could include the dcedfs/syscall.h which should have these
* numbers, but it has extra baggage. So for
* simplicity sake now, we define these here.
*/
#define AFSCALL_SETPAG 2
#define AFSCALL_GETPAG 11
#if defined(sun)
#define AFS_SYSCALL 72
#elif defined(hpux)
/* assume HPUX 10 + or is it 50 */
#define AFS_SYSCALL 326
#elif defined(_AIX)
#define DPAGAIX "dpagaix"
/* #define DPAGAIX "/krb5/sbin/dpagaix" */
#elif defined(sgi) || defined(_sgi)
#define AFS_SYSCALL 206+1000
#else
#define AFS_SYSCALL (Unknown_DFS_AFS_SYSCALL)
#endif
static sigjmp_buf setpag_buf;
static sigtype mysig()
{
siglongjmp(setpag_buf, 1);
}
int krb5_dfs_newpag(new_pag)
int new_pag;
{
handler sa1, osa1;
handler sa2, osa2;
int pag = -1;
handler_init (sa1, mysig);
handler_init (sa2, mysig);
handler_swap (SIGSYS, sa1, osa1);
handler_swap (SIGSEGV, sa2, osa2);
if (sigsetjmp(setpag_buf, 1) == 0) {
#if defined(_AIX)
int (*dpagaix)(int, int, int, int, int, int);
if (dpagaix = load(DPAGAIX, 0, 0))
pag = (*dpagaix)(AFSCALL_SETPAG, new_pag, 0, 0, 0, 0);
#else
pag = syscall(AFS_SYSCALL,AFSCALL_SETPAG, new_pag, 0, 0, 0, 0);
#endif
handler_set (SIGSYS, osa1);
handler_set (SIGSEGV, osa2);
return(pag);
}
fprintf(stderr,"Setpag failed with a system error\n");
/* syscall failed! return 0 */
handler_set (SIGSYS, osa1);
handler_set (SIGSEGV, osa2);
return(-1);
}
main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
extern char *optarg;
int rv;
int rc;
unsigned int pag;
unsigned int newpag = 0;
char ccname[256];
int nflag = 0;
while((rv = getopt(argc,argv,"n:")) != -1) {
switch(rv) {
case 'n':
nflag++;
sscanf(optarg,"%8x",&newpag);
break;
default:
printf("Usage: k5dcepagt -n pag \n");
exit(1);
}
}
if (nflag) {
fprintf (stderr,"calling k5dcepag newpag=%8.8x\n",newpag);
pag = krb5_dfs_newpag(newpag);
fprintf (stderr,"PAG returned = %8.8x\n",pag);
if ((pag != 0) && (pag != -1)) {
sprintf (ccname,
"FILE:/opt/dcelocal/var/security/creds/dcecred_%8.8x",
pag);
setenv("KRB5CCNAME",ccname,1);
execl("/bin/csh","csh",0);
}
else {
fprintf(stderr," Not a good pag value\n");
}
}
}