580 lines
27 KiB
HTML
580 lines
27 KiB
HTML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE html
|
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html lang="en-us" xml:lang="en-us">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<meta name="security" content="public" />
|
|
<meta name="Robots" content="index,follow" />
|
|
<meta http-equiv="PICS-Label" content='(PICS-1.1 "http://www.icra.org/ratingsv02.html" l gen true r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true r (n 0 s 0 v 0 l 0) "http://www.classify.org/safesurf/" l gen true r (SS~~000 1))' />
|
|
<meta name="DC.Type" content="topic" />
|
|
<meta name="DC.Title" content="ISV code examples" />
|
|
<meta name="DC.Relation" scheme="URI" content="rzamzenablessoisv.htm" />
|
|
<meta name="DC.Relation" scheme="URI" content="rzamztestyourapplication.htm" />
|
|
<meta name="copyright" content="(C) Copyright IBM Corporation 2000, 2006" />
|
|
<meta name="DC.Rights.Owner" content="(C) Copyright IBM Corporation 2000, 2006" />
|
|
<meta name="DC.Format" content="XHTML" />
|
|
<meta name="DC.Identifier" content="rzamzisvsnippet" />
|
|
<meta name="DC.Language" content="en-us" />
|
|
<!-- All rights reserved. Licensed Materials Property of IBM -->
|
|
<!-- US Government Users Restricted Rights -->
|
|
<!-- Use, duplication or disclosure restricted by -->
|
|
<!-- GSA ADP Schedule Contract with IBM Corp. -->
|
|
<link rel="stylesheet" type="text/css" href="./ibmdita.css" />
|
|
<link rel="stylesheet" type="text/css" href="./ic.css" />
|
|
<title>ISV code examples</title>
|
|
</head>
|
|
<body id="rzamzisvsnippet"><a name="rzamzisvsnippet"><!-- --></a>
|
|
<!-- Java sync-link --><script language="Javascript" src="../rzahg/synch.js" type="text/javascript"></script>
|
|
<h1 class="topictitle1">ISV code examples</h1>
|
|
<div><p>IBM<sup>®</sup> grants
|
|
you a nonexclusive copyright license to use all programming code examples
|
|
from which you can generate similar function tailored to your own specific
|
|
needs.</p>
|
|
<p>All sample code is provided by IBM for illustrative purposes only. These
|
|
examples have not been thoroughly tested under all conditions. IBM, therefore,
|
|
cannot guarantee or imply reliability, serviceability, or function of these
|
|
programs.</p>
|
|
<p>All programs contained herein are provided to you "AS IS" without any warranties
|
|
of any kind. The implied warranties of non-infringement, merchantability and
|
|
fitness for a particular purpose are expressly disclaimed.</p>
|
|
<div class="note"><span class="notetitle">Note:</span> By using the code examples, you agree to the terms of the <a href="codedisclaimer.htm">Code license and disclaimer information</a>.</div>
|
|
<pre>/*** START OF SPECIFICATIONS *************************************/
|
|
/* */
|
|
/* MODULE NAME: Kerberos/EIM server sample */
|
|
/* */
|
|
/* DESCRIPTION: Below is sample code for writing a kerberos server */
|
|
/* along with calling EIM apis to map from a kerberos */
|
|
/* principal to an OS/400 user profile. */
|
|
/* */
|
|
/* NOTE: Error checking has been removed. */
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
/* #include files removed here */
|
|
|
|
//---------------------------------------------------------------------
|
|
// EIM assumptions:
|
|
// On the OS400 where this program is running the EIM configuration
|
|
// information has been set. The infomration used by this program
|
|
// is:
|
|
// - ldapURL
|
|
// - local registry
|
|
// EIM ldap lookup connection
|
|
// - The ldap connection information needed for doing the mapping
|
|
// lookups in this program could be stored in a validation list
|
|
// or other user secure space. Here we will just hard code
|
|
// pretend values.
|
|
// - This connection will only be used for a lookup operation so
|
|
// the ldap user only needs EIM mapping lookup authroity.
|
|
// All EIM data (Identifiers, associations, etc) has been added.
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
#define LDAP_BINDDN "cn=mydummmy"
|
|
#define LDAP_BINDPW "special"
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Function: l_eimError
|
|
// Purpose: EIM error has occurred. This function will print out the
|
|
// EIM error message.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
void l_eimError(char * function, EimRC * err)
|
|
{
|
|
char * msg = NULL;
|
|
printf("EIM ERROR for function = %s.\n", function);
|
|
msg = eimErr2String(err);
|
|
printf(" %s\n",msg);
|
|
free(msg);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Function: l_eimConnect
|
|
// Purpose: Get an EIM handle and connect to the ldap server.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
int l_eimConnect(EimHandle * handle)
|
|
{
|
|
int rc = 0;
|
|
char eimerr[150];
|
|
EimRC *err = (EimRC *)&eimerr;
|
|
EimConnectInfo con;
|
|
|
|
/* This needs to be at least 48. */
|
|
err->memoryProvidedByCaller = 150;
|
|
|
|
//------------------------------------------------------------------
|
|
// Create handle. We will pass NULL for the URL indicating that we
|
|
// will use the information that was configured for the system.
|
|
//------------------------------------------------------------------
|
|
eimCreateHandle(handle,
|
|
NULL,
|
|
err);
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
// Connect
|
|
//------------------------------------------------------------------
|
|
// The ldap user id and password could be stored in a validation
|
|
// list or other user secure space. Here we will just hard code
|
|
// pretend values.
|
|
// You can also choose to use Kerberos authentication when
|
|
// connecting to ldap. You will first need to verify your ldap
|
|
// server is set up to accept kerberos authentication.
|
|
//------------------------------------------------------------------
|
|
// This connection will only be used for a lookup operation so the
|
|
// ldap user only needs EIM mapping lookup authroity.
|
|
//------------------------------------------------------------------
|
|
con.type = EIM_SIMPLE;
|
|
con.creds.simpleCreds.protect = EIM_PROTECT_NO;
|
|
con.creds.simpleCreds.bindDn = LDAP_BINDDN;
|
|
con.creds.simpleCreds.bindPw = LDAP_BINDPW;
|
|
con.ssl = NULL;
|
|
eimConnect(handle,
|
|
con,
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Function: getOS400User
|
|
// Purpose: Get OS400 user associated with the kerberos user and swap
|
|
// to the user.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
int getOS400User(EimHandle * handle,
|
|
char * OS400User,
|
|
gss_buffer_desc * client_name)
|
|
{
|
|
|
|
char * principal;
|
|
char * realm;
|
|
char * atsign;
|
|
|
|
//------------------------------------------------------------------
|
|
//
|
|
// Get principal and realm from the kerberos client_name.
|
|
//
|
|
//------------------------------------------------------------------
|
|
// client_name.value contains string of principal@realm. Get
|
|
// pointer to each piece.
|
|
//------------------------------------------------------------------
|
|
principal = client_name->value;
|
|
atsign = strchr(principal, '@');
|
|
*atsign = 0x00; // NULL terminate the principal
|
|
realm = atsign + 1; // ASdvance pointer to the realm
|
|
|
|
//------------------------------------------------------------------
|
|
//
|
|
// Call EIM to get the target user associated with the kerberos
|
|
// source user. This sample application assumes that the
|
|
// kerberos realm name is also the name of the EIM registry
|
|
// defining this realm.
|
|
//
|
|
//------------------------------------------------------------------
|
|
listPtr = (EimList *)listBuff;
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (0 != (rc =
|
|
eimGetTargetFromSource(handle,
|
|
realm,
|
|
principal,
|
|
NULL, // use configured
|
|
// local
|
|
// registry.
|
|
NULL,
|
|
listSize,
|
|
listPtr,
|
|
err)))
|
|
{
|
|
l_eimError("eimGetTargetFromSource", err);
|
|
return -1;
|
|
}
|
|
|
|
if (listPtr->bytesAvailable == listPtr->bytesReturned)
|
|
break;
|
|
else
|
|
{
|
|
listSize = listPtr->bytesAvailable;
|
|
freeStorage = malloc(listSize);
|
|
listPtr = (EimList *)freeStorage;
|
|
}
|
|
}
|
|
|
|
// Check the number of entries found, if 0 no mapping exists...
|
|
// otherwise extract user profile from buffer and cleanup
|
|
// storage
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Function Name: get_kerberos_credentials_for_server */
|
|
/* */
|
|
/* Descriptive Name: Basically this function finds the keytab entry */
|
|
/* for this server. It will use this to validate */
|
|
/* the tokens received. */
|
|
/* */
|
|
/* Input: char * service_name - the service name. */
|
|
/* gss_buffer_t msg_buf - the input message */
|
|
/* Output: */
|
|
/* gss_cred_id_t *server_creds - The output credential */
|
|
/* */
|
|
/* Exit Normal: return value == 0 */
|
|
/* Exit Error: -1, error was encountered, */
|
|
/********************************************************************/
|
|
int get_kerberos_credentials_for_server (
|
|
char * service_name, /* name of service principal */
|
|
gss_cred_id_t * server_creds) /* credential acquired */
|
|
{
|
|
gss_buffer_desc name_buf; /* buffer for import name */
|
|
gss_name_t server_name; /* gss service name */
|
|
OM_uint32 maj_stat, /* GSS status code */
|
|
min_stat; /* Mechanism kerberos status */
|
|
|
|
/* Convert service name to GSS internal format */
|
|
name_buf.value = service_name;
|
|
name_buf.length = strlen((char *)name_buf.value) + 1;
|
|
maj_stat = gss_import_name(
|
|
&min_stat, /* kerberos status */
|
|
&name_buf, /* name to convert */
|
|
(gss_OID) gss_nt_service_name, /* name type */
|
|
&server_name); /* GSS internal name */
|
|
|
|
/* Acquire credentials for the service from keytab */
|
|
maj_stat = gss_acquire_cred(
|
|
&min_stat, /* kerberos status */
|
|
server_name, /* gss internal name */
|
|
GSS_C_INDEFINITE, /* max credential life */
|
|
GSS_C_NULL_OID_SET, /* use default mechanism */
|
|
GSS_C_ACCEPT, /* credential usage */
|
|
server_creds, /* output cred handle */
|
|
NULL, /* ignore actual mech */
|
|
NULL); /* ignore time remaining */
|
|
|
|
/* Release the gss internal format name */
|
|
gss_release_name(&min_stat, &server_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Function Name: do_kerberos_authentication() */
|
|
/* Purpose: Any valid client request is accepted. If a context */
|
|
/* is established, its handle is returned in context and */
|
|
/* the client name is returned. */
|
|
/* */
|
|
/* Exit Normal: return value == 0 */
|
|
/* Exit Error: -1, error was encountered, */
|
|
/********************************************************************/
|
|
int do_kerberos_authentication (
|
|
int s, /* socket connection */
|
|
gss_cred_id_t server_creds, /* credentials for the server */
|
|
gss_ctx_id_t * context, /* GSS context */
|
|
gss_buffer_t client_name) /* kerberos principal */
|
|
{
|
|
gss_buffer_desc send_tok, /* token to send to client */
|
|
recv_tok; /* token received from client */
|
|
gss_name_t client; /* client principal */
|
|
OM_uint32 maj_stat, /* GSS status code */
|
|
min_stat; /* Mechanism (kerberos) status */
|
|
msgDesc_t msgSend, /* Message buffer to send */
|
|
msgRecv; /* Message buffer received */
|
|
gss_OID doid;
|
|
|
|
*context = GSS_C_NO_CONTEXT; /* initialize the context */
|
|
|
|
do {
|
|
/* Receive the message from the client */
|
|
memset(&msgRecv, 0x00, sizeof(msgRecv));
|
|
if (0 != recvAmessage(s, &msgRecv))
|
|
return -1;
|
|
recv_tok.length = msgRecv.dataLength;
|
|
recv_tok.value = msgRecv.buffer;
|
|
|
|
/* Accept the secuirty context */
|
|
maj_stat = gss_accept_sec_context(
|
|
&min_stat, /* kerberos status */
|
|
context, /* context handle */
|
|
server_creds, /* acquired server creds */
|
|
&recv_tok, /* token received */
|
|
GSS_C_NO_CHANNEL_BINDINGS, /* no CB */
|
|
&client, /* client requestor */
|
|
NULL, /* ignore mech type */
|
|
&send_tok, /* token to be sent */
|
|
NULL, /* ignore ctx flags */
|
|
NULL, /* ignore time_rec */
|
|
NULL); /* ignore delegated cred */
|
|
|
|
/* release the received token */
|
|
gss_release_buffer(&min_stat, &recv_tok);
|
|
|
|
/* Check to see if there is a token client wants mutual
|
|
authentication. */
|
|
if (send_tok.length != 0)
|
|
{
|
|
|
|
/* Send the token message to the other side */
|
|
/* release the send token buffer */
|
|
}
|
|
} while (maj_stat == GSS_S_CONTINUE_NEEDED);
|
|
|
|
/* client name is returned - extract client from ticket. This
|
|
client name will be used to map to the OS400 user profile */
|
|
maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
|
|
|
|
maj_stat = gss_release_name(&min_stat, &client);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Function Name: getTestPort() */
|
|
/* */
|
|
/* Descriptive Name: get the port on which the server is listening */
|
|
/* */
|
|
/* Input: char * service - the service name. If null, looks */
|
|
/* for kerb-test-server. */
|
|
/* */
|
|
/* Output: none */
|
|
/* */
|
|
/* Exit Normal: return value == port number */
|
|
/* */
|
|
/* Exit Error: N/A */
|
|
/* */
|
|
/********************************************************************/
|
|
CLINKAGE int getTestPort(char *name)
|
|
{
|
|
struct servent service;
|
|
struct servent_data servdata;
|
|
char defaultName[] = "krb-test-server", *servName;
|
|
char tcp[] = "tcp";
|
|
int retPort, rc;
|
|
memset(&servdata, 0x00, sizeof(servdata));
|
|
memset(&service, 0x00, sizeof(service));
|
|
if (name == NULL)
|
|
servName = defaultName;
|
|
else
|
|
servName = name;
|
|
rc = getservbyname_r(servName, tcp, &service,
|
|
&servdata);
|
|
if (rc != 0)
|
|
retPort = DEFAULT_KERB_SERVER_PORT;
|
|
else
|
|
retPort = service.s_port;
|
|
|
|
|
|
return ntohl(retPort);
|
|
} /* end getPort */
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Function Name: getListeningSocket() */
|
|
/* */
|
|
/* Descriptive Name: get a listening socket created and return it. */
|
|
/* */
|
|
/* Input: none. */
|
|
/* */
|
|
/* Output: listening socket created. */
|
|
/* */
|
|
/* Exit Normal: return value == listening socket. */
|
|
/* */
|
|
/* Exit Error: -1, error was encountered. */
|
|
/* */
|
|
/* NOTE: Error checking removed */
|
|
/* */
|
|
/********************************************************************/
|
|
CLINKAGE int getListeningSocket(void)
|
|
{
|
|
int rc, sd, option;
|
|
struct sockaddr_in sin;
|
|
|
|
sd = socket(AF_INET, SOCK_STREAM, 0)
|
|
|
|
option = 1;
|
|
|
|
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
|
|
(char *)&option, sizeof(option));
|
|
|
|
memset(&sin, 0x00, sizeof(sin));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons(getTestPort(NULL));
|
|
|
|
bind(sd, (struct sockaddr *)&sin, sizeof(sin));
|
|
|
|
listen(sd, SOMAXCONN);
|
|
|
|
return sd;
|
|
} /* end getListeningSocket() */
|
|
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Function Name: getServerSocket() */
|
|
/* */
|
|
/* Descriptive Name: get a server socket that is connected to a */
|
|
/* client. This routine blocks waiting for */
|
|
/* the client. */
|
|
/* */
|
|
/* Input: int lsd - listening socket. */
|
|
/* */
|
|
/* Output: server socket created. */
|
|
/* */
|
|
/* Exit Normal: return value == server socket. */
|
|
/* */
|
|
/* Exit Error: -1, error was encountered. */
|
|
/* */
|
|
/* NOTE: Error checking removed */
|
|
/* */
|
|
/* */
|
|
/********************************************************************/
|
|
CLINKAGE int getServerSocket(int lsd)
|
|
{
|
|
return accept(lsd, NULL, 0);
|
|
} /* end getServerSocket() */
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Function Name: main */
|
|
/* */
|
|
/* Descriptive Name: Driver for the server program which performs */
|
|
/* kerberos authentication and EIM mapping. */
|
|
/* */
|
|
/* Input: char* service_name - name of service requested */
|
|
/* */
|
|
/* Exit Normal: 0 = success */
|
|
/* */
|
|
/* Exit Error: -1, error was encountered. */
|
|
/* */
|
|
/* NOTE: Error checking removed */
|
|
/* */
|
|
/* */
|
|
/********************************************************************/
|
|
int main(int argc, char **argv)
|
|
{
|
|
int ssd, /* server socket */
|
|
lsd; /* listening socket */
|
|
char *service_name; /* name of service (input) */
|
|
gss_cred_id_t server_creds; /* server credentials to acquire */
|
|
gss_ctx_id_t context; /* GSS context */
|
|
OM_uint32 maj_stat, /* GSS status code */
|
|
min_stat; /* Mechanism (kerberos) status */
|
|
gss_buffer_desc client_name; /* Client principal establishing
|
|
context. */
|
|
char OS400User[10];
|
|
char save_handle[SY_PH_MAX_PRFHDL_LEN]; // *CURRENT profile handle
|
|
char client_handle[SY_PH_MAX_PRFHDL_LEN];// Swap to profile handle
|
|
EimHandle eimHandle;
|
|
|
|
Qus_EC_t errorcode;
|
|
memset(errorcode, 0x00, 256);
|
|
errorcode->Bytes_Provided = 256;
|
|
|
|
service_name = argv[1];
|
|
|
|
/*------------------------------------------------------------------
|
|
// Kerberos setup
|
|
// Acquire credentials for the service
|
|
//----------------------------------------------------------------*/
|
|
get_kerberos_credentials_for_server(service_name, &server_creds);
|
|
|
|
/*------------------------------------------------------------------
|
|
// get a listening socket
|
|
//----------------------------------------------------------------*/
|
|
lsd = getListeningSocket();
|
|
|
|
/*------------------------------------------------------------------
|
|
// EIM setup
|
|
// Connect to eim
|
|
// ----------------------------------------------------------------*/
|
|
l_eimConnect(&eimHandle);
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
// Save a copy of the current user so we can swap back to it
|
|
// after each request
|
|
// ----------------------------------------------------------------*/
|
|
QsyGetProfileHandleNoPwd(save_handle,
|
|
"*CURRENT ",
|
|
"*NOPWD ",
|
|
&errorcode);
|
|
|
|
/*------------------------------------------------------------------
|
|
// Loop waiting for requests on the socket
|
|
//----------------------------------------------------------------*/
|
|
do { /* loop until the application or the system is ended */
|
|
/* Save the profile handle of the current user */
|
|
/* Accept a TCP connection */
|
|
ssd = getServerSocket(lsd);
|
|
|
|
/* -----------------------------------------------------------------
|
|
// Establish context with the client and get the client name.
|
|
//------------------------------------------------------------------
|
|
// The client name contains the kerberos principal and realm. In
|
|
// EIM these equate to the source user and source registry.
|
|
//--------------------------------------------------------------- */
|
|
do_kerberos_authentication(ssd,
|
|
server_creds,
|
|
&context,
|
|
&client_name);
|
|
|
|
/*------------------------------------------------------------------
|
|
// Perform eim mapping lookup operation to get the associated
|
|
// OS400 user.
|
|
//--------------------------------------------------------------- */
|
|
getOS400User(&eimHandle,
|
|
OS400User,
|
|
&client_name);
|
|
|
|
/* ---------------------------------------------------------------------
|
|
// Swap to the user returned from EIM lookup
|
|
// ---------------------------------------------------------------- */
|
|
QsyGetProfileHandleNoPwd(client_handle,
|
|
client_name,
|
|
"*NOPWDCHK ",
|
|
&errorcode);
|
|
QsySetToProfileHandle(client_handle, &errorcode);
|
|
|
|
/* ---------------------------------------------------------------------
|
|
// do the real work of the application here as the application is
|
|
// now running under an appropriate user profile
|
|
// ---------------------------------------------------------------- */
|
|
// Call or code application specific behavior here.
|
|
|
|
/* ---------------------------------------------------------------------
|
|
// reset the process to run under the original user profile
|
|
// ---------------------------------------------------------------- */
|
|
QsySetToProfileHandle(save_handle, &errorcode);
|
|
|
|
} while (1)
|
|
|
|
eimDestroy_handle(&eimHandle);
|
|
|
|
gss_delete_sec_context(&min_stat, &context, NULL);
|
|
close(ssd);
|
|
close(lsd);
|
|
gss_release_cred(&min_stat, &server_creds);
|
|
return 0;
|
|
}
|
|
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<div class="familylinks">
|
|
<div class="parentlink"><strong>Parent topic:</strong> <a href="rzamzenablessoisv.htm" title="View this information to review scenarios that illustrate typical single signon implementation situations to help you plan your own certificate implementation as part of your server security policy.">Scenario: Enable single signon for ISV applications</a></div>
|
|
<div class="previouslink"><strong>Previous topic:</strong> <a href="rzamztestyourapplication.htm">Test your application</a></div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html> |