diff options
Diffstat (limited to 'base/native-tools/src/setpin/setpin.c')
-rw-r--r-- | base/native-tools/src/setpin/setpin.c | 1237 |
1 files changed, 1237 insertions, 0 deletions
diff --git a/base/native-tools/src/setpin/setpin.c b/base/native-tools/src/setpin/setpin.c new file mode 100644 index 000000000..f1bf6a8c7 --- /dev/null +++ b/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); + } +} + + + |