/* -*- C -*- * * Copyright 2000 Compaq Computer Corporation * * COMPAQ Registered in U.S. Patent and Trademark Office. * * Consistent with FAR 12.211 and 12.212, Commercial Computer Software, * Computer Software Documentation, and Technical Data for Commercial Items * are licensed to the U.S. Government under vendor's standard commercial * license. * * This software is subject to change without notice and is provided "AS IS" * WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK ARISING OUT OF THE USE OF * THIS SOFTWARE REMAINS WITH RECIPIENT. IN NO EVENT SHALL COMPAQ BE LIABLE * FOR ANY DIRECT, CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE OR OTHER * DAMAGES WHATSOEVER (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF * BUSINESS PROFITS, BUSINESS INTERRUPTION OR LOSS OF BUSINESS INFORMATION), * EVEN IF COMPAQ HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE * FOREGOING SHALL APPLY REGARDLESS OF THE NEGLIGENCE OR OTHER FAULT OF * EITHER PARTY AND REGARDLESS OF WHETHER SUCH LIABILITY SOUNDS IN CONTRACT, * NEGLIGENCE, TORT, OR ANY OTHER THEORY OF LEGAL LIABILITY, AND * NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. * * The limited warranties for Compaq products are exclusively set forth in * the documentation accompanying such products. Nothing herein should be * construed as constituting a further or additional warranty. * * */ /* * Compaq revision history * * Rev Author Date Comments * --- ------ ---- -------- * 001 Nick Hudson 08-Sep-2000 Initial version * * 002 Kevin Greaney 03-Feb-2005 Add in SSL functionality. */ /* * This program demonstrates the use of the OpenVMS LDAP API from a client * application. * * The program may be compiled using either C or C++. * * The program may be compiled with either 32 or 64 bit pointers (providing * that the compiler supports it). * * To build this program use: * $ cc ldap_example * $ link ldap_example * * The program expects to run as a foreign command. To define the foreign * command use the following syntax: * * $ ldap_example := $disk1:[mydir]ldap_example.exe ! define foreign command * * The program expects the following arguments: * * server The node which is providing LDAP access to a directory * * base The base object in the directory for the search operation * * filter The search filter to be used * * attributes An optional list of one or more attributes to be returned * for each matching record. If no attributes are specified, * then all user attributes will be returned. * * An example of a search command would be: * * $ ldap_example server "o=acme, c=us" "(sn=s*)" cn sn * $ ldap_example hitech.zko.dec.com "dc=hp, dc=com" "(uid=*)" userid * * Given the parameters above, the program will attempt to make contact * with an LDAP server on node "server", and request a search for all * records below the object "o=acme, c=us" that match the filter "sn=s*". * For each matching record, the attributes "cn" and "sn" will be displayed. * * * SSL Notes * --------- * - First, make sure that SSL is installed and started on the system * that this program will be running from as well as the system on * which the directory server resides. SSL is installed with a * PCSI kit. Then you need to start SSL to define some symbols * and install some images: $ @SYS$STARTUP:SSL111$STARTUP.COM. $ @SSL111$COM:SSL111$UTILS * * - Create a Certificate Authority (CA) certificate. * The filename of the CA can be anything you want it to be. You can * create the CA certificate using the SSL certificate tool: * $ @SSL111$COM:SSL111$CERT_TOOL * * Then select option 5 - Create a CA (Certification Authority) Certificate PEM Pass Phrase ? [] Confirm PEM Pass Phrase ? [] Encryption Bits ? [1024] Default Days ? [1825] CA certificate Key File ? [SSL111$KEY:SERVER_CA.KEY] SSL111$ROOT:[DEMOCA.CERTS]DSA-CA.KEY ! ! this contains the passphrase for the CA; when you sign the server certificate ! and it asks you for a passphrase, this is where it is going to look. KEEP THIS SAFE NEXT TO YOUR HEART. CA certificate File ? [SSL111$CRT:SERVER_CA.CRT] SSL111$ROOT:[DEMOCA.CERTS]DSA-CA.CRT ! ! AKA: ca_file = [directory]cacert.pem ! IOW: the .CRT is a .PEM (format) file ! THIS IS THE FILE YOU ARE GOING TO COPY TO THE CLIENT ***IN A SECURE FASHION**** Country Name ? [US] Organization Name ? [] hp Organization Unit Name ? [] ossg_qtv Common Name ? [CA Authority] Require Unique Subject Names? [yes] no Display the CA certificate ? [N] * * - Create a certificate for the directory server you will be connecting to. * In this example, we used the Enterprise Directory directory server. * The filename of directory server certificate has to be DSA-CERTIFICATE.PEM, and the filename of the * directory server private key has to be DSA-PRIVATE-KEY.PEM. Both of these files need to reside * in DXD$DIRECTORY. You first need to create a certificate request. You will then sign that * request with the CA certificate created above. * * Select option 3 - Create a Certificate Signing Request This step creates a Private key for use by the server. Encrypt Private Key ? [N] Yes < normally, if you say yes, you would need to type in a password each time that the server accesses the Server's private key file (in this case DXD$DIRECTORY:DSA-PRIVATE-KEY.PEM). With X500, you have to set the DSA characteristic PRIVATE KEY PASSPHRASE and it has to match this private key encryption passphrase.> PEM Pass Phrase ? [] Confirm PEM Pass Phrase ? [] Encryption Bits ? [1024] Certificate Key File ? [SSL111$KEY:SERVER.KEY] DXD$DIRECTORY:DSA-PRIVATE-KEY.PEM ! Server's private key file Certificate Request File ? [SSL111$CSR:SERVER.CSR] DXD$DIRECTORY:DSA-PRIVATE-KEY.csr ! Server's private key file CSR ! this is a temporary file used by step 6; after that you can delete it. Country Name ? [US] State or Province Name ? [] nh City Name ? [] nashua Organization Name ? [] hp Organization Unit Name ? [] ossg_qtv Common Name ? [fbr4.zko.dec.com] Email Address ? [webmaster@fbr4.zko.dec.com] Display the Certificate ? [N] * * Now, you just need to sign the request with the CA certificate. * Select option 6 - Sign a Certificate Signing Request CA Certificate File ? [SSL111$CRT:SERVER_CA.CRT] SSL111$ROOT:[DEMOCA.CERTS]DSA-CA.CRT CA Certificate Key File ? [SSL111$KEY:SERVER_CA.KEY] SSL111$ROOT:[DEMOCA.CERTS]DSA-CA.KEY Certificate Request File ? [SSL111$CSR:SERVER.CSR] DXD$DIRECTORY:DSA-PRIVATE-KEY.csr Signed Certificate File ? [SSL111$CRT:SIGNED.CRT] DXD$DIRECTORY:DSA-CERTIFICATE.PEM Default Days ? [624] 3000 PEM Pass Phrase ? [] Display the Certificate ? [N] * * - Copy the CA certificate over to the system that will be making the LDAP calls. * If you are following the above example, you the CA certificate will be in SSL111$CERTS. * This LDAP example expects the CA certificate to be in the same directory as the .C code. * $ COPY SSL111$CERTS:DSA-CA.CRT node::ddcu:[dir] * * * - This example used an Enterprise Directory directory server. The * following settings were used to make this example work: * * $ run sys$system:dxd$ncl.exe * NCL> disable DSA * NCL> set dsa ssl ldsap security protocol SSLv3 * NCL> set dsa private key passphrase "password2" ! Note : This is the same password used to encrypt the private key. * NCL> ! of the DSA server certificate. * NCL> set dsa ssl state on * NCL> enable dsa * NCL> exit * $ */ #include #include #include #include /* * Generic routine to display an error message and return any * supplementary information from the LDAP handle */ void report_error(const char *operation, int result, LDAP *ld) { int stat; char *errmsgp = NULL; printf("%s returned error %d (\"%s\")\n",operation, result, ldap_err2string(result)); stat = ldap_get_option(ld,LDAP_OPT_ERROR_STRING,&errmsgp); if ((stat == -1) || (strlen(errmsgp) == 0)) { printf("No supplementary error text available\n"); } else { printf("Error text : \"%s\"\n",errmsgp); } if (errmsgp != NULL) { ldap_memfree(errmsgp); errmsgp = NULL; } } void main(int argc, char *argv[]) { int stat; int i; int num_entries; char **attrs_array = NULL; int attribute_count; /* * For servers which don't support version 3 of the protocol, edit * the line below to use LDAP_VERSION2. Both of these constants * are defined inside LDAP.H */ int protocol = LDAP_VERSION3; /* * The following pointers may be allocated by the LDAP API, and * should be free'd using an appropriate mechanism */ LDAP *ld = NULL; LDAPMessage *result = NULL; LDAPMessage *one_entry = NULL; char *dn = NULL; char *attr_name = NULL; char **values = NULL; BerElement *ber = NULL; char* ldap_opt_tls_ca_file = "hitech_ca.crt"; char* ldap_opt_tls_cert_file = "hitech_dsa.crt"; char* ldap_opt_tls_pkey_file = "hitech_dsa.key"; int startTls = 1; /* 0 for dedicated SSL, 1 for negotiated */ /* TLS_v1 = 1 or 31 SSL_v2 = 20 SSL_v23 = 32 SSL_v3 = 30 */ int LDAP_SSL_version = 30; if (argc < 4) { printf("Usage : %s [server] [base] [filter] \n",argv[0]); goto finished; } /* * If the user requested any particular attributes, then build up * an array to pass to the search routine. If no attributes are * specified, then "attrs_array" will be NULL, and the server should * return all user attributes for each matching entry */ attribute_count = (argc - 4); if (attribute_count != 0) { i = 0; /* * Allocate enough room for a pointer to each attribute, plus * a NULL terminating pointer. */ attrs_array = (char **)calloc((attribute_count + 1),sizeof(char *)); while (i < attribute_count) { attrs_array[i] = strdup(argv[i+4]); i++; } } /* * Establish a context with the library specifying an LDAP server name * and using the default LDAP port number. * No connection is made with the server at this stage, so a failure here * would indicate a problem with the library, rather than a network or * server related issue. */ ld = ldap_init(argv[1], LDAP_PORT); if (ld == NULL) { printf("ldap_init failed\n"); goto finished; } /* * Having been provided with a LDAP context, it is possible now to * specify options for this session */ /* the default is ldapV2, which doesn't support TLS, so set it to V3 */ stat = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option",stat,ld); goto finished; } /* * Now, let's try to add in the SSL piece. This will involve * setting some extra options as well as making an extra call * to the LDAP APIs. */ if (ldap_opt_tls_ca_file != NULL) { /* stat = ldap_set_option(ld, LDAP_OPT_TLS_CERT_REQUIRED, LDAP_OPT_ON); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option tls #1",stat,ld); goto finished; } */ stat = ldap_set_option(ld, LDAP_OPT_TLS_VERIFY_REQUIRED, LDAP_OPT_ON); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option tls #2",stat,ld); goto finished; } stat = ldap_set_option(ld, LDAP_OPT_TLS_VERSION, &LDAP_SSL_version); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option tls #3",stat,ld); goto finished; } stat = ldap_set_option(ld, LDAP_OPT_TLS_CA_FILE, ldap_opt_tls_ca_file); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option tls #4",stat,ld); goto finished; } /* stat = ldap_set_option(ld, LDAP_OPT_TLS_CERT_FILE, ldap_opt_tls_cert_file); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option tls #4",stat,ld); goto finished; } stat = ldap_set_option(ld, LDAP_OPT_TLS_PKEY_FILE, ldap_opt_tls_pkey_file); if (stat != LDAP_SUCCESS) { report_error("ldap_set_option tls #4",stat,ld); goto finished; } */ } stat = ldap_tls_start(ld, startTls); if (stat != LDAP_SUCCESS) { report_error("ldap_tls_start",stat,ld); goto finished; } /* * Execute a simple bind, without specifying any authorization * information, i.e. an anonymous bind */ stat = ldap_simple_bind_s(ld,NULL,NULL); if (stat != LDAP_SUCCESS) { report_error("simple_bind",stat,ld); goto finished; } /* * Issue a synchronous search request; this call will not return * until the server has responded. */ stat = ldap_search_s(ld, /* ld */ argv[2], /* base */ LDAP_SCOPE_SUBTREE, /* scope */ argv[3], /* filter */ attrs_array, /* attrs */ 0, /* attrsonly */ &result); /* res */ if (stat != LDAP_SUCCESS) { report_error("ldap_search_s",stat,ld); goto finished; } num_entries = ldap_count_entries(ld,result); if (num_entries == 0) { printf("No matching entries found\n"); goto finished; } printf("Number of entries returned : %d\n",num_entries); /* * Now look at the results that came back */ one_entry = ldap_first_entry(ld,result); while (one_entry != NULL) { /* * The distinguished name of this entry */ dn = ldap_get_dn(ld,one_entry); if (dn != NULL) { printf("\ndn = %s\n",ldap_get_dn(ld,one_entry)); ldap_memfree(dn); dn = NULL; /* * Step through each attribute */ attr_name = ldap_first_attribute(ld,one_entry,&ber); if (attr_name == NULL) { printf("\n"); } /* * Extract the values for each attribute returned. This program * assumes that such values will be "printable" value strings. For * non-printable (binary) data, ldap_get_values_len() should * be used. */ while (attr_name != NULL) { values = ldap_get_values(ld,one_entry,attr_name); if (values == NULL) { printf("\n"); } else { i = 0; while (values[i] != NULL) { printf("%s : %s\n",attr_name,values[i]); i++; } } ldap_memfree(attr_name); attr_name = NULL; ldap_value_free(values); values = NULL; attr_name = ldap_next_attribute(ld,one_entry,ber); } /* * The BerElement pointer is no longer needed and so can be * released. Since this pointer was just being used as an * iterator and doesn't point to any memory that the program * has allocated itself, use zero as the second parameter to * ber_free(). */ if (ber != NULL) { ber_free(ber, 0); ber = NULL; } } else { report_error("ldap_get_dn",0,ld); } one_entry = ldap_next_entry(ld,one_entry); } /* * All exit paths should come through here, where free up any data * structures that the library has allocated using the appropriate * functions. * It is a good habit to set pointers to NULL after releasing them, * although in the cases below it isn't strictly necessary. */ finished: /* * "dn" may have been allocated by ldap_get_dn() */ if (dn != NULL) { ldap_memfree(dn); dn = NULL; } /* * "attr_name" may have been allocated by ldap_first_attribute() or * ldap_next_attribute() */ if (attr_name != NULL) { ldap_memfree(attr_name); attr_name = NULL; } /* * "values" may have been allocated by ldap_get_values() */ if (values != NULL) { ldap_value_free(values); values = NULL; } /* * "ber" may have been allocated by ldap_first_attribute() or * ldap_next_attribute() * Since this pointer was just being used as an iterator and * doesn't point to any memory that the program has allocated * itself, use zero as the second parameter to ber_free() */ if (ber != NULL) { ber_free(ber, 0); ber = NULL; } /* * "one_entry" may have been allocated by ldap_first_entry() or * ldap_next_entry() */ if (one_entry != NULL) { ldap_msgfree(one_entry); one_entry = NULL; } /* * "result" may have been allocated by ldap_search_s() */ if (result != NULL) { ldap_msgfree(result); result = NULL; } /* * "ld" was returned from ldap_init() */ if (ld != NULL) { ldap_unbind(ld); ld = NULL; } /* * "attrs_array" was allocated by this program */ if (attrs_array != NULL) { i = 0; while (attrs_array[i] != NULL) { free(attrs_array[i]); attrs_array[i] = NULL; i++; } free(attrs_array); attrs_array = NULL; } printf("\nProgram terminating\n"); }