IBM® 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.
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.
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.
/*** 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; }