summaryrefslogtreecommitdiffstats
path: root/pki/base/native-tools/src/setpin/setpin.c
diff options
context:
space:
mode:
Diffstat (limited to 'pki/base/native-tools/src/setpin/setpin.c')
-rw-r--r--pki/base/native-tools/src/setpin/setpin.c1237
1 files changed, 1237 insertions, 0 deletions
diff --git a/pki/base/native-tools/src/setpin/setpin.c b/pki/base/native-tools/src/setpin/setpin.c
new file mode 100644
index 0000000..f1bf6a8
--- /dev/null
+++ b/pki/base/native-tools/src/setpin/setpin.c
@@ -0,0 +1,1237 @@
+/* --- BEGIN COPYRIGHT BLOCK ---
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ * All rights reserved.
+ * --- END COPYRIGHT BLOCK ---
+ */
+
+
+/* Set-pin tool */
+
+
+/* This will modify the specified attribute in the directory
+ You must add the pin objectclass to the schema
+ e.g in config/slapd.oc.conf
+
+ attribute pin bin
+ objectclass pinPerson
+ superior organizationalPerson
+ allows
+ pin
+*/
+
+/*
+ History:
+ version 1.2 - upgraded to NSS 3.3.1
+ */
+
+#define SETPIN_VERSION "1.2"
+
+#include "options.h"
+#include "setpin_options.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <ldap.h>
+
+#define USE_NSS_RANDOM
+
+#ifdef USE_NSS_RANDOM
+/* removed #include <secrng.h> as of NSS 3.9 */
+/* removed from nss3_3_1 secrngt.h*/
+typedef struct RNGContextStr RNGContext;
+#endif
+
+#include <sechash.h>
+
+#include <plhash.h>
+#include <prerror.h>
+#include <ctype.h>
+
+#include <secoidt.h>
+#include <nss.h>
+
+extern int equals(char *s, char *t);
+extern SECStatus PK11_HashBuf(SECOidTag hashAlg,
+ unsigned char *out,
+ unsigned char *in,
+ int32 len);
+extern SECStatus PK11_GenerateRandom(unsigned char *data,
+ int len);
+
+/* use NSS's new generic hash api */
+#define USE_NSS_GEN_HASH
+
+void exitError(char *errstring);
+void exitLDAPError(char *errstring);
+void doLDAPBind();
+void doLDAPSearch(LDAPMessage **result);
+void doLDAPUnbind();
+void processSearchResults(LDAPMessage *r);
+char *newPassword();
+void initrandom();
+void testpingen();
+void do_setup();
+
+
+char *sha1_pw_enc( char *pwd );
+
+int errcode=0;
+
+LDAP *ld=NULL;
+char *programName = NULL;
+
+FILE *output;
+FILE *input;
+
+
+PLHashTable *pinHashTable=NULL;
+
+#ifdef USE_NSS_RANDOM
+RNGContext *rngc = NULL;
+#endif
+
+/* this tool should really be changed to use NSPR */
+#ifdef _WIN32
+#define strcasecmp stricmp
+#endif
+
+void exitError(char *errstring) {
+ char *errbuf;
+
+ errbuf = malloc(strlen(errstring)+strlen(programName)+10);
+
+ sprintf(errbuf,"%s error : %s\n",programName,errstring);
+ fputs(errbuf,stderr);
+ exit(errcode);
+}
+
+
+void exitLDAPError(char *errstring) {
+ char *ldaperr;
+ char *newerror;
+ int err;
+
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
+ ldaperr = ldap_err2string(err);
+ newerror = (char*) malloc((errstring?strlen(errstring):0) + (ldaperr?strlen(ldaperr):0) +5);
+ sprintf(newerror,"%s (%s)",errstring?errstring:"",ldaperr?ldaperr:"");
+ exitError(newerror);
+}
+
+
+/* This returns an allocated string, like strdup does, except that
+ the duplicate string begins with the first non-whitespace character */
+
+char * trim_strdup(char *s)
+{
+ while (*s == ' ' || *s == '\t') {
+ s++;
+ }
+ if (*s == '\0') return NULL;
+ return strdup(s);
+}
+
+void readInputFile() {
+ int more_to_read=1;
+ char *thedn = NULL;
+ char *thepin = NULL;
+ int linenum=0;
+
+ pinHashTable = PL_NewHashTable(256,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ NULL, /* allocOps */
+ NULL);
+ if (pinHashTable == NULL) {
+ errcode=9;
+ exitError("Couldn't create dn->pin hashtable");
+ }
+
+ if (o_input) {
+
+ do {
+ char line[4096];
+ char *n;
+ char *checkdn;
+
+ do {
+ n = fgets(line,4096,input);
+ linenum++;
+ if (! n) {
+ more_to_read = 0;
+ break;
+ }
+
+ /* replace newline with null byte */
+
+ line[strlen(line)-1] = 0;
+
+ if (! strncmp("dn:",line,3)) {
+ thedn = trim_strdup(&line[3]);
+ if (thedn == NULL) {
+ fprintf(stderr,"warning: empty line not allowed at line: %d\n",linenum);
+ }
+ }
+
+ if (! strncmp("pin:",line,4)) {
+ thepin = trim_strdup(&line[4]);
+ }
+
+ } while (strlen(line));
+
+ /* first check to see if that dn is already in the hashtable */
+
+ if (thepin == NULL) {
+ thepin = strdup("");
+ }
+
+ if (thedn && thepin) {
+
+ checkdn = (char*) PL_HashTableLookup(pinHashTable, thedn);
+ if (checkdn) {
+ char msg[256];
+ errcode = 10;
+ strcpy(msg,"Duplicate entry in input file for dn=");
+ strcat(msg,thedn);
+ exitError(msg);
+ }
+
+ PL_HashTableAdd(pinHashTable,
+ thedn,
+ thepin);
+ fprintf(stderr, "Reading dn/pin ( %s, %s )\n", thedn, thepin);
+ if (o_debug) {
+ fprintf(stderr, "Reading dn/pin ( %s, %s )\n", thedn, thepin);
+ }
+
+ } else {
+ if (o_debug) {
+ fprintf(stderr," ...ignoring\n");
+ }
+ }
+ if (thedn != NULL) {
+ free(thedn);
+ thedn = NULL;
+ }
+ if (thepin != NULL) {
+ free(thepin);
+ thepin = NULL;
+ }
+ } while (more_to_read);
+ }
+}
+
+
+int main(int ac, char **av) {
+ char *error;
+ LDAPMessage *search_results;
+
+ programName = av[0];
+ if (strlen(av[0]) == 0) {
+ strcpy(programName, "setpin");
+ }
+ else {
+ strcpy(programName, av[0]);
+ }
+
+ if (ac == 1) {
+ int i=0;
+ fprintf(stderr,"Setpin utility. Version " SETPIN_VERSION "\n"
+ "(C) 2005 Fedora Project.\n\n");
+ fprintf(stderr,"To set up directory for pin usage, modify setpin.conf, "
+ "then run:\n %s optfile=<svr_root>/bin/cert/tools/setpin.conf\n", programName);
+ fprintf(stderr,"\nUsage: %s option=value ... option=value\n\n", programName);
+
+ for (i = 0; i < valid_args_len; i += 2) {
+ if (valid_args[i]) {
+ fprintf(stderr,"%13s : %s\n",valid_args[i],valid_args[i+1]);
+ } else {
+ errcode=0;
+ fprintf(stderr,"\n");
+ exit(errcode);
+ }
+ }
+ }
+
+ error = OPT_parseOptions(ac, av, valid_args);
+ if (error) {
+ errcode=7;
+ exitError(error);
+ }
+
+ setDefaultOptions();
+
+ getOptions();
+ fprintf(stderr,"\n");
+ if (o_debug) {
+ fprintf(stderr,"about to validateOptions\n");
+ }
+
+ validateOptions();
+
+ /* Initialize random number generator */
+ initrandom();
+
+ if (o_debug) {
+ fprintf(stderr,"about to doLDAPBind\n");
+ }
+
+ if (! o_testpingen) {
+ doLDAPBind();
+ }
+
+ if (o_setup) {
+ do_setup();
+ }
+
+ if (o_output) {
+ output = fopen(o_output,"w");
+ if (!output) {
+ errcode=5;
+ exitError("Couldn't open output file");
+ }
+ } else {
+ output = stdout;
+ }
+
+ if (o_testpingen) {
+ testpingen();
+ exit(0);
+ }
+
+ if (o_input) {
+ input = fopen(o_input,"r");
+ if (!input) {
+ errcode=8;
+ exitError("Couldn't open input file");
+ }
+ }
+
+ readInputFile();
+
+ if (o_debug) {
+ fprintf(stderr,"about to doLDAPSearch\n");
+ }
+
+ doLDAPSearch(&search_results);
+
+ if (o_debug) {
+ fprintf(stderr,"about to processSearchResults\n");
+ }
+
+ processSearchResults(search_results);
+
+ if (output != stdout) {
+ fclose(output);
+ }
+
+ return 0;
+}
+
+
+
+/* This function implements the 'setup' procedure, invoked when the user
+ specified 'setup' as one of the arguments. The point is that in this
+ mode, schema modifications are performed to add these things to the
+ directory schema:
+ if (schemachange argument is specified)
+ - 'pin' attribute as specified by the 'attribute' argument (default 'pin')
+ - 'pinPerson' objectclass as specified by the 'objectclass argument (dfl: pinperson)
+ if ('pinmanager' argument specified)
+ - pin manager user, with permission to remove the pin for the basedn specified
+
+*/
+
+void do_setup() {
+ int i;
+
+ char *x_values[]={NULL,NULL,NULL};
+ char *a1_values[]={NULL,NULL};
+ char *a2_values[]={NULL,NULL};
+ char *a3_values[]={NULL,NULL};
+ char *a4_values[]={NULL,NULL};
+ LDAPMod x,a1,a2,a3,a4;
+ LDAPMod *mods[10];
+ char* password=NULL;
+ int err;
+
+ x_values[0] = malloc(1024);
+
+ doLDAPBind();
+
+ if (o_schemachange) {
+ sprintf(x_values[0],"( %s-oid NAME '%s' DESC 'User Defined Attribute' SYNTAX '1.3.6.1.4.1.1466.115.121.1.5' SINGLE-VALUE )",
+ o_attribute,
+ o_attribute);
+
+ fprintf(stderr,"Adding attribute: %s\n",x_values[0]);
+ x_values[1] = NULL;
+ x.mod_op = LDAP_MOD_ADD;
+ x.mod_type = "attributetypes";
+ x.mod_values = x_values;
+ mods[0] = &x;
+ mods[1] = NULL;
+
+ i = ldap_modify_ext_s(ld, "cn=schema", mods, NULL, NULL);
+
+ if (i != LDAP_SUCCESS) {
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
+ if (err != LDAP_TYPE_OR_VALUE_EXISTS) {
+ exitLDAPError("couldn't modify schema when creating pin attribute");
+ } else {
+ fprintf(stderr," .. successful\n\n");
+ }
+ }
+
+ sprintf(x_values[0],"( %s-oid NAME '%s' DESC 'User Defined ObjectClass' SUP 'top' MUST ( objectclass ) MAY ( aci $ %s )",
+ o_objectclass,o_objectclass,
+ o_attribute);
+
+ fprintf(stderr,"Adding objectclass: %s\n",x_values[0]);
+
+ x_values[1] = NULL;
+ x.mod_op = LDAP_MOD_ADD;
+ x.mod_type = "objectclasses";
+ x.mod_values = x_values;
+ mods[0] = &x;
+ mods[1] = NULL;
+
+
+ i = ldap_modify_ext_s(ld, "cn=schema", mods, NULL, NULL);
+
+ if (i != LDAP_SUCCESS) {
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
+ if (err != LDAP_TYPE_OR_VALUE_EXISTS) {
+ exitLDAPError("couldn't modify schema when creating objectclass");
+ } else {
+ fprintf(stderr," .. successful\n\n");
+ }
+ }
+ }
+
+ if (o_pinmanager) {
+
+ if (o_pinmanagerpwd == NULL) {
+ exitError("missing pinmanagerpwd argument");
+ }
+ if (o_basedn == NULL) {
+ exitError("missing basedn argument");
+ }
+
+ password = sha1_pw_enc( o_pinmanagerpwd );
+
+ fprintf(stderr,"Adding user: %s\n",o_pinmanager);
+
+ a1_values[0] = "pinmanager";
+ a1_values[1] = NULL;
+ a1.mod_op = 0;
+ a1.mod_type = "sn";
+ a1.mod_values = a1_values;
+
+ a2_values[0] = "pinmanager";
+ a2_values[1] = NULL;
+ a2.mod_op = 0;
+ a2.mod_type = "cn";
+ a2.mod_values = a2_values;
+
+ a3_values[0] = password;
+ a3_values[1] = NULL;
+ a3.mod_op = 0;
+ a3.mod_type = "userPassword";
+ a3.mod_values = a3_values;
+
+ a4_values[0] = "person";
+ a4_values[1] = NULL;
+ a4.mod_op = 0;
+ a4.mod_type = "objectclass";
+ a4.mod_values = a4_values;
+
+ mods[0] = &a1;
+ mods[1] = &a2;
+ mods[2] = &a3;
+ mods[3] = &a4;
+ mods[4] = NULL;
+
+
+ i = ldap_add_ext_s(ld, o_pinmanager, mods, NULL, NULL);
+
+ if (i != LDAP_SUCCESS) {
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
+ if (!( err == LDAP_TYPE_OR_VALUE_EXISTS || err == LDAP_ALREADY_EXISTS)) {
+ exitLDAPError("couldn't create new user");
+ } else {
+ fprintf(stderr," .. successful\n\n");
+ }
+ }
+
+
+ /* modify aci on basedn to allow pinmanager to modify pin attr */
+
+ fprintf(stderr,"modifying ACI for: %s\n",o_basedn);
+
+ sprintf(x_values[0],"(target=\"ldap:///%s\")"
+ "(targetattr=\"pin\")"
+ "(version 3.0; acl \"Pin attribute\"; "
+ "allow (all) userdn = \"ldap:///%s\"; "
+ "deny(proxy,selfwrite,compare,add,write,delete,search) "
+ "userdn = \"ldap:///self\"; ) ",
+ o_basedn,
+ o_pinmanager);
+
+ x_values[1] = malloc(1024);
+
+ sprintf(x_values[1],"(target=\"ldap:///%s\")"
+ "(targetattr=\"objectclass\")"
+ "(version 3.0; acl \"Pin Objectclass\"; "
+ "allow (all) userdn = \"ldap:///%s\"; "
+ " ) ",
+ o_basedn,
+ o_pinmanager);
+
+ x_values[2] = NULL;
+ x.mod_op = LDAP_MOD_ADD;
+ x.mod_type = "aci";
+ x.mod_values = x_values;
+
+ mods[0] = &x;
+ mods[1] = NULL;
+
+ i = ldap_modify_ext_s(ld, o_basedn, mods, NULL, NULL);
+
+ if (i != LDAP_SUCCESS) {
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
+ if (!( err == LDAP_TYPE_OR_VALUE_EXISTS || err == LDAP_ALREADY_EXISTS)) {
+ exitLDAPError("couldn't modify aci on basedn");
+ } else {
+ fprintf(stderr," .. successful\n\n");
+ }
+ }
+ }
+ exit(0);
+}
+
+int ldif_base64_encode(
+ unsigned char *src, char *dst, int srclen, int lenused );
+
+/* do password hashing */
+
+/*
+ * Number of bytes each hash algorithm produces
+ */
+#define SHA1_LENGTH 20
+
+
+char *
+sha1_pw_enc( char *pwd )
+{
+ unsigned char hash[ SHA1_LENGTH ];
+ char *enc;
+
+ /* SHA1 hash the user's key */
+ PK11_HashBuf(SEC_OID_SHA1,hash,pwd,strlen(pwd));
+ enc = malloc(256);
+
+ sprintf( enc, "{SHA}");
+
+ (void)ldif_base64_encode( hash, enc + 5,
+ SHA1_LENGTH, -1 );
+
+ return( enc );
+}
+
+/* check the first 8 characters to see if this is a string */
+
+int isstring(char *s) {
+ int i=0;
+
+ for (i=0;i<8;i++) {
+ if (*s == 0) return 1;
+ if (! isprint(*s)) return 0;
+ s++;
+ }
+ return 1;
+}
+
+
+void doLDAPBind() {
+ char errbuf[1024];
+ char ldapuri[1024];
+ int port=389;
+ int r;
+ int status;
+
+ if (o_port == NULL) {
+ if (o_ssl) {
+ port = 636;
+ /* fprintf(stderr,"o_ssl = %0x, o_certdb = %0x, o_nickname= %0x\n",o_ssl,o_certdb,o_nickname); */
+ } else {
+ port = 389;
+ }
+ } else {
+ port = atoi(o_port);
+ }
+
+ if (o_debug) {
+ fprintf(stderr,"# connecting to %s:%d\n",o_host,port);
+ }
+
+ if (o_ssl) {
+ printf("SSL not currently supported.\n");
+ exit(0);
+ /* ld = ldapssl_init(o_host,port,LDAPSSL_AUTH_CNCHECK); */
+ } else {
+ snprintf(ldapuri, 1024, "ldap://%s:%i", o_host, port);
+ status = ldap_initialize(&ld, ldapuri);
+ }
+
+ if ((status != LDAP_SUCCESS) || (ld == NULL)) {
+ errcode=4;
+ exitError("could not connect to directory server");
+ }
+
+ if (o_debug) {
+ fprintf(stderr,"# ldap_init completed\n");
+ }
+
+ struct berval credential;
+ credential.bv_val = o_bindpw;
+ credential.bv_len= strlen(o_bindpw);
+
+ r = ldap_sasl_bind_s(ld, o_binddn, LDAP_SASL_SIMPLE, &credential, NULL, NULL, NULL);
+ if (r != LDAP_SUCCESS) {
+ sprintf(errbuf,"could not bind to %s:%d as %s",o_host,port,o_binddn);
+ if (strstr(o_binddn,"=") == NULL) {
+ strcat(errbuf,". Perhaps you missed the 'CN=' part of the bin DN?");
+ }
+ exitLDAPError(errbuf);
+ }
+
+ if (o_debug) {
+ fprintf(stderr,"# ldap_simple_bind_s completed\n");
+ }
+}
+
+
+void doLDAPSearch(LDAPMessage **result ) {
+ int r;
+ char errbuf[1024];
+
+ r = ldap_search_ext_s( ld, o_basedn, LDAP_SCOPE_SUBTREE,
+ o_filter, NULL, 0, NULL, NULL, NULL, 0, result );
+
+ if (r != LDAP_SUCCESS ) {
+ sprintf(errbuf,"could not complete search with that filter. Check filter and basedn");
+ exitLDAPError(errbuf);
+ }
+
+ if (o_debug) {
+ fprintf(stderr,"# ldap_search_s completed\n");
+ }
+}
+
+void doLDAPUnbind(){
+ ldap_unbind_ext_s(ld, NULL, NULL);
+}
+
+
+void processSearchResults(LDAPMessage *r) {
+ LDAPMessage *e;
+ char *dn;
+ char *a;
+ struct berval **vals;
+#ifdef USE_NSS_GEN_HASH
+ /* HASHContext *hcx;
+ HASH_HashType ht; */
+#else
+#endif
+ int i;
+ BerElement *ber;
+ char *objectclass_values[]={NULL,NULL};
+ int change=0;
+ int pin_objectclass_exists=0;
+ LDAPMod objectclass, pinattribute;
+ LDAPMod *mods[3];
+ SECStatus status = SECFailure;
+
+ char *saltval;
+ int action;
+ char *hashbuf_source = NULL;
+ char hashbuf_dest[256];
+ char errbuf[1024];
+ int pindatasize= 0;
+ char *pindata = NULL;
+ char *generatedPassword = NULL;
+ struct berval *bvals[2];
+ struct berval bval;
+
+ bvals[0] = &bval;
+ bvals[1] = NULL;
+
+ /* Check whether any results were found. */
+ i = ldap_count_entries( ld, r );
+
+ fprintf(stderr,"filter %s found %d matching results.\n", o_filter,i);
+
+ /* for each entry print out name + all attrs and values */
+ for ( e = ldap_first_entry( ld, r ); e != NULL;
+ e = ldap_next_entry( ld, e ) ) {
+
+ generatedPassword = NULL;
+
+ if ( (dn = ldap_get_dn( ld, e )) != NULL ) {
+ fprintf(stderr, "Processing: %s\n", dn );
+ if (o_input) {
+ generatedPassword = (char*) PL_HashTableLookup(pinHashTable,dn);
+ if (generatedPassword) {
+ fprintf(stderr, " found user from input file\n");
+ }
+ if (! generatedPassword) {
+ fprintf(stderr, " Skipping (not in input file)\n");
+ continue;
+ }
+ }
+ }
+
+
+ /* what we do here is go through all the entries looking for
+ 'objectclass'.
+ */
+
+ pin_objectclass_exists = 0;
+ change = 0;
+
+#define ACTION_NONE 0
+#define ACTION_REPLACE 1
+#define ACTION_ADD 2
+
+ action = ACTION_ADD;
+
+ saltval = NULL;
+ /* loop through the entries */
+ for ( a = ldap_first_attribute( ld, e, &ber );
+ a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {
+
+ if ((vals = ldap_get_values_len( ld, e, a)) != NULL ) {
+
+ if (o_debug && (! strcasecmp(o_debug,"attrs"))) {
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ char *bin;
+ bin = "<binary>";
+ if (isstring(vals[i]->bv_val)) {
+ bin = vals[i]->bv_val;
+ }
+
+ fprintf(stderr, " %s: %s\n",a,bin);
+ }
+ }
+
+ if (o_debug) {
+ fprintf(stderr," examining attribute: %s\n",a);
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ fprintf(stderr," val[%d]: %s\n",i,vals[i]->bv_val);
+ }
+ }
+
+ if (o_saltattribute != NULL) {
+ if (!strcasecmp(a,o_saltattribute)) {
+ saltval = vals[0]->bv_val;
+ if (o_debug) {
+ fprintf(stderr," setting salt value to: %s\n",saltval);
+ }
+ }
+ }
+
+ if (!strcasecmp(a,"objectclass")) {
+ /* check if we have a pin objectclass already */
+ /* Cycle through all the values for this
+ entry, looking for the one which matches the
+ objectclass we specified */
+
+ /* if user specified objectclass= on the commandline,
+ without any value, then the objectclass is assumed to
+ exist already */
+ if (strlen(o_objectclass) == 0) {
+ if (o_debug) { fprintf(stderr, " user objectclass assumed to already exist\n"); }
+ pin_objectclass_exists=1;
+ } else {
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ if (o_debug) {
+ fprintf(stderr, " checking vals[%d]=%s == objectclass=%s -> %d \n",
+ i,vals[i]->bv_val, o_objectclass, strcasecmp(vals[i]->bv_val,o_objectclass));
+ }
+ if (!strcasecmp(vals[i]->bv_val,o_objectclass)) {
+ if (o_debug) {
+ fprintf(stderr, " %s: %s found\n", a, vals[i]->bv_val );
+ }
+ pin_objectclass_exists = 1;
+ }
+ }
+ }
+ } else if (!strcasecmp(a,o_attribute)) {
+ if (o_clobber) {
+ action = ACTION_REPLACE;
+ } else {
+ action = ACTION_NONE;
+ }
+ }
+
+ /* use ldap_value_free_len */
+ ldap_value_free_len( vals );
+ }
+ ldap_memfree( a );
+ }
+
+ if (o_debug) { fprintf(stderr, " Did the objectclass exist? %d\n", pin_objectclass_exists); }
+
+ /* add the objectclass attribute if it doesn't already exist */
+
+ if (! pin_objectclass_exists) {
+ if (o_debug) {
+ fprintf(stderr,"objectclass: %s doesn't exist, adding\n",o_objectclass);
+ }
+ objectclass_values[0] = o_objectclass;
+ objectclass_values[1] = NULL;
+ objectclass.mod_op = LDAP_MOD_ADD;
+ objectclass.mod_type = "objectclass";
+ objectclass.mod_values = objectclass_values;
+ mods[0] = &objectclass;
+ mods[1] = NULL;
+
+ if (o_write) {
+ i = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
+
+ if (i != LDAP_SUCCESS) {
+ exitLDAPError("couldn't modify attribute");
+ }
+ }
+ }
+
+ pinattribute.mod_type = o_attribute;
+
+ /* password could have been set from input file. If not, set it now */
+ if (generatedPassword == NULL || (strlen(generatedPassword) == 0)) {
+ generatedPassword = newPassword();
+ }
+ if (generatedPassword == NULL || (strlen(generatedPassword) == 0)) {
+ errcode=13;
+ exitError("Couldn't generate password.");
+ }
+
+ /* should we hash the password? */
+ if (o_hash) {
+
+ /* we hash the DN of the user and the PIN together */
+
+ if (o_debug) {
+ fprintf(stderr,"checking salt attribute...\n");
+ }
+ if (saltval == NULL) {
+ if (o_saltattribute != NULL) {
+ errcode = 11;
+ exitError("specified salt attribute not found for this user");
+ }
+ if (o_debug) {
+ fprintf(stderr,"setting salt attribute to dn...\n");
+ }
+ saltval = dn;
+ }
+
+ hashbuf_source =
+ malloc(strlen(saltval) + strlen(generatedPassword) + 10);
+ if (hashbuf_source == NULL) {
+ errcode=12;
+ exitError("Couldn't allocate 'hashbuf_source'.");
+ }
+ strcpy(hashbuf_source,saltval);
+ strcat(hashbuf_source,generatedPassword);
+
+ if (o_debug) {
+ fprintf(stderr,"hashing this: %s\n",hashbuf_source);
+ }
+
+ saltval = NULL;
+
+ /* We leave one byte at the beginning of the hash
+ buffer, to support the hash type */
+
+#define SENTINEL_SHA1 0
+#define SENTINEL_MD5 1
+#define SENTINEL_NONE '-'
+
+ if ((!strcmp(o_hash,"SHA1")) || (!strcmp(o_hash,"sha1")) ) {
+ status = PK11_HashBuf(SEC_OID_SHA1,
+ (unsigned char *)hashbuf_dest+1,
+ (unsigned char *)hashbuf_source,
+ strlen(hashbuf_source)
+ );
+ hashbuf_dest[0] = SENTINEL_SHA1;
+ pindatasize = SHA1_LENGTH + 1;
+ } else if ((!strcmp(o_hash,"MD5")) || (!strcmp(o_hash,"md5")) ) {
+ status = PK11_HashBuf(SEC_OID_MD5,
+ (unsigned char *)hashbuf_dest+1,
+ (unsigned char *)hashbuf_source,
+ strlen(hashbuf_source)
+ );
+ hashbuf_dest[0] = SENTINEL_MD5;
+ pindatasize = MD5_LENGTH + 1;
+ } else if ((!strcmp(o_hash,"NONE")) || (!strcmp(o_hash,"none")) ) {
+ hashbuf_dest[0] = SENTINEL_NONE;
+ status = SECSuccess;
+ memcpy(hashbuf_dest+1,
+ hashbuf_source,
+ strlen(hashbuf_source)
+ );
+ } else {
+ sprintf(errbuf,"Unsupported hash type '%s'. Must be one of 'sha1', 'md5' or 'none",o_hash);
+ errcode = 7;
+ exitError(errbuf);
+ }
+
+ if (status != SECSuccess) {
+ sprintf(errbuf,"Error hashing pin (%d)",PR_GetError());
+ errcode = 9;
+ exitError(errbuf);
+ }
+
+ pindata = hashbuf_dest;
+
+ if (hashbuf_source != NULL) {
+ free(hashbuf_source);
+ hashbuf_source = NULL;
+ }
+ } else {
+ pindata = generatedPassword;
+ pindatasize = strlen(generatedPassword);
+ }
+
+ bval.bv_len = pindatasize;
+ bval.bv_val = pindata;
+
+ fprintf(stderr," Adding new %s\n",o_attribute);
+
+ if (! o_write) {
+ fprintf(stderr, " [NOTE: 'write' was not specified, so no changes will be made to the directory]\n");
+ }
+
+ pinattribute.mod_bvalues = bvals;
+ if (action == ACTION_REPLACE) {
+ pinattribute.mod_op = LDAP_MOD_REPLACE|LDAP_MOD_BVALUES;
+ if (o_debug) {
+ fprintf(stderr," %s exists, replacing\n",o_attribute);
+ }
+ } else if (action == ACTION_ADD) {
+ if (o_debug) {
+ fprintf(stderr," %s doesn't exist, adding\n",o_attribute);
+ }
+ pinattribute.mod_op = LDAP_MOD_ADD|LDAP_MOD_BVALUES;
+ } else if (action == ACTION_NONE) {
+ if (o_debug) {
+ fprintf(stderr," %s exists. not replacing\n",o_attribute);
+ }
+ goto skip_write;
+ }
+ mods[0] = &pinattribute;
+ mods[1] = NULL;
+
+ if (o_write) {
+ i = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
+
+ if (i != LDAP_SUCCESS) {
+ exitLDAPError("couldn't modify attribute");
+ }
+ }
+
+ skip_write:
+
+ fprintf(output,"dn:%s\n",dn);
+ fprintf(output,"%s:%s\n",o_attribute,generatedPassword);
+ if (o_debug) {
+ fprintf(stderr,"o_write = %0x\n",(unsigned int)o_write);
+ }
+ if (! o_write) {
+ fprintf(output,"status:notwritten\n");
+ } else {
+ if (action == ACTION_NONE) {
+ fprintf(output,"status:notreplaced\n");
+ } else {
+ if (i != LDAP_SUCCESS) {
+ fprintf(output,"status:writefailed\n");
+ } else {
+ if (action == ACTION_ADD) {
+ fprintf(output,"status:added\n");
+ } else if (action == ACTION_REPLACE) {
+ fprintf(output,"status:replaced\n");
+ }
+ }
+ }
+ }
+
+ fprintf(output,"\n");
+
+ if (dn) {
+ ldap_memfree( dn );
+ dn = NULL;
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 0 );
+ }
+ fprintf(stderr, "\n" );
+ }
+ ldap_msgfree( r );
+}
+
+
+/* this function uses i_minlength and i_maxlength to determine the
+ size of the password to generate */
+
+static char *UCalpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+
+static char *LCalpha = "abcdefghijklmnopqrstuvwxyz";
+static char *numbers = "0123456789";
+static char *punc = "!#$%&*+,-./:;<=>?@[]^{|}";
+
+static char *charpool = NULL; /* carpool, geddit? */
+static int charpoolsize;
+
+static char *RNG_ALPHA = "RNG-alpha";
+static char *RNG_PRINTABLEASCII = "RNG-printableascii";
+static char *RNG_ALPHANUM = "RNG-alphanum";
+
+
+/* build the pool of characters we can use for the password */
+
+void buildCharpool() {
+ char err_buf[1024];
+ charpool = (char*) malloc(256);
+
+ charpool[0] = '\0';
+
+ if ( o_case == NULL) {
+ strcat(charpool,LCalpha); /* then add the lowercase */
+ } else {
+ if (strcmp(o_case,"upperonly")) {
+ errcode = 7;
+ exitError("Illegal value for case=");
+ }
+ }
+
+
+ if ( !strcmp(o_gen,RNG_ALPHA) ||
+ !strcmp(o_gen,RNG_ALPHANUM) ||
+ !strcmp(o_gen,RNG_PRINTABLEASCII) ) {
+ strcat(charpool,UCalpha); /* add uppercase chars */
+ } else {
+ sprintf(err_buf,"invalid value '%s' for gen= option",o_gen);
+ errcode = 7;
+ exitError(err_buf);
+ }
+
+ if ( strcmp(o_gen,"RNG-alpha")) { /* not alpha-only */
+ strcat(charpool,numbers);
+ }
+ if (! strcmp(o_gen,"RNG-printableascii")) {
+ strcat(charpool, punc);
+ }
+ if (o_debug) {
+ fprintf(stderr,"Character pool: %s\n",charpool);
+ }
+ charpoolsize = strlen(charpool);
+}
+
+
+/* initialize random number generator */
+
+void initrandom() {
+ char err_buf[1024];
+#ifdef USE_NSS_RANDOM
+ if( NSS_Initialize( "",
+ "",
+ "",
+ "",
+ NSS_INIT_NOCERTDB |
+ NSS_INIT_NOMODDB |
+ NSS_INIT_FORCEOPEN ) != SECSuccess ) {
+ sprintf(err_buf,"Couldn't initialize NSS (error code %d)\n",PR_GetError());
+ errcode = 9;
+ exitError(err_buf);
+ }
+#else
+ srand(time(NULL));
+#endif
+
+}
+
+
+unsigned short getRandomShort() {
+ unsigned short r;
+#ifdef USE_NSS_RANDOM
+ PK11_GenerateRandom( ( unsigned char * ) &r, sizeof( r ) );
+ if (o_debug) {
+ /* fprintf(stderr,"Random: %d\n",r); */
+ }
+ return r;
+#else
+ return (unsigned short) rand();
+#endif
+}
+
+
+/*
+ * this function is important. It needs review.
+ *
+ * returns a random number in the range (0 .. max-1)
+ */
+
+/* We have a short, rno, and we want to convert this to a number
+ in the required range by just using (rno % max). However,
+ this may result in some of the numbers at the end of 'rno's
+ range being selected more frequently. So, if random number
+ select is in this range, we will pick another.
+
+ As an example, assume:
+ a short is 4 bits (0..15)
+ max is 6
+
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ a a a a a a b b b b b b X X X X
+
+ we want to reject everything more than 11
+
+ we take 16 (that largest number which can be in a short+1)
+ divide by 'max', which is 6. This gives us 2. Multiply by
+ max, gives us 12. Subtract 1, which is 11, our highest
+ allowable range. Now we do the modulus.
+
+*/
+
+unsigned short getRandomInRange(unsigned short max) {
+ unsigned short rno;
+ unsigned short result;
+
+ unsigned short max_allowed_rno =
+ ((65536 / max) * max) -1;
+
+ do {
+ rno = getRandomShort();
+ } while (rno >max_allowed_rno);
+
+ result = rno % max;
+
+ assert(result < max);
+
+ return result;
+}
+
+
+char * newPassword() {
+ static char *pw_buf=NULL;
+ unsigned short l;
+ unsigned short r;
+ int i;
+
+ if (pw_buf == NULL) {
+ pw_buf = (char *) malloc(i_maxlength+5);
+ }
+
+ if (charpool == NULL) {
+ buildCharpool();
+ }
+
+ /* decide how long the password should be */
+ /* It must be between i_minlength and i_maxlength */
+
+ if (i_minlength == i_maxlength) {
+ l = i_minlength;
+ } else {
+ l = getRandomInRange((unsigned short)(1 + i_maxlength - i_minlength));
+ l += i_minlength;
+ }
+
+ for (i=0; i<l; i++) {
+ r = getRandomInRange((unsigned short)(charpoolsize));
+ pw_buf[i] = charpool[r];
+ }
+ pw_buf[l] = '\0';
+ return pw_buf;
+}
+
+
+void testpingen() {
+ int count=25;
+ int i,j;
+ int pwlen;
+ char *pw;
+ unsigned int index[256];
+ unsigned int *totals;
+ char c;
+
+ if (! equals(o_testpingen,"")) {
+ count = atoi(o_testpingen);
+ }
+
+ if (charpool == NULL) {
+ buildCharpool();
+ }
+
+ /* last spot is used to hold invalid chars */
+ totals = malloc(sizeof(int)*(charpoolsize+1));
+ if (totals != NULL) {
+ for (i=0;i<(charpoolsize);i++) {
+ totals[i] = 0;
+ }
+ totals[charpoolsize]=0;
+ for (i=0;i<256;i++) {
+ index[i] = 255; /* indicates->invalid */
+ }
+ for (i=0;i<charpoolsize;i++) {
+ index[(int)(charpool[i])] = i;
+ }
+
+ for (i=0;i<count;i++) {
+ pw = newPassword();
+ if (pw != NULL) {
+ if (o_debug) {
+ fprintf(output,"%d:%s\n",i+1,pw);
+ }
+ pwlen = strlen(pw);
+ for (j=0;j<pwlen;j++) {
+ c = pw[j];
+ if (index[(int)c] == 255) {
+ printf("\ninvalid char found: %02x %c\n",c,c);
+ totals[charpoolsize]++;
+ }
+ else {
+ totals[index[(int)c]]++;
+ }
+ }
+ free(pw);
+ }
+ }
+
+ for (i=0;i<charpoolsize;i++) {
+ fprintf(output,"%c: %10d\n",charpool[i],totals[i]);
+ }
+ fprintf(output,"invalid: %10d\n",totals[charpoolsize]);
+ free(totals);
+ }
+}
+
+
+