#!/usr/bin/ksh93 ################################################################ function usagemsg_mkrandpwd_k93 { print " Program: mkrandpwd_k93 This script generates a random password string Usage: ${1##*/} [-?] [-vV] [-luadsn] [-m minimum] [-M maximum] [-O minother] [-A minalpha] [-R maxrepeats] [-L minlower] [-U minupper] [-D mindigit] [-S minsymbol] Where: -l = Convert upper case letters to lower case in random password string. -u = Convert lower case letters to upper case in random password string. -a = Only return alphabetic characters in random password string. -d = Only return numeric digits in random password string. -s = Only return punctuation symbols in random password string. -n = Only return alphanumeric characters in random password string. -m minimum = Specify the minimum number of characters to be returned in the random password. Default: 8 -M maximum = Specify the maximum number of characters to be returned in the random password string. Default: 8 -O minother = Specify the minimum number of non-alphabetic characters in the random password. Default: 0 -A minalpha = Specify the minimum number of alphabetic characters in the random password. Default: 0 -L minlower = Specify the minimum number of lowercase letters in the random password. Default: 0 -U minupper = Specify the minimum number of uppercase letters in the random password. Default: 0 -D mindigit = Specify the minimum number of numeric digit characters in the random password. Default: 0 -S minsymbol = Specify the minimum number of punctuation symbol characters in the random password. Default: 0 -R maxrepeats = Specify the maximum number of times a character can be repeated in the random password string. Default: 8 -v = Verbose mode -V = Very Verbose Mode Example: mkrandpwd_k93 Author: Dana French (dfrench@mtxia.com) Copyright 2006 by Dana French \"AutoContent\" enabled " } ################################################################ function mkrandpwd_k93 { typeset TRUE="1" typeset FALSE="0" typeset RETCODE="0" typeset VERBOSE="${FALSE}" typeset VERYVERB="${FALSE}" typeset AONLY="${FALSE}" typeset DONLY="${FALSE}" typeset NONLY="${FALSE}" typeset SONLY="${FALSE}" typeset LONLY="${FALSE}" typeset UONLY="${FALSE}" typeset MINIMUM="8" typeset MAXIMUM="${MINIMUM}" typeset MAXLEN="8" typeset MINOTHER="0" OTHER="-1" typeset MINLOWER="0" LOWER="-1" typeset MINUPPER="0" UPPER="-1" typeset MINALPHA="0" ALPHA="-1" typeset MINDIGIT="0" DIGIT="-1" typeset MINSYMBOL="0" SYMBOL="-1" typeset MAXREPEATS="8" REPEATS="-1" typeset POSCNT typeset RANDSTR typeset ITERATION="0" #### #### Set the array of valid characters to be all upper and #### lower case letters, all digits, and a selected set of #### punctuation symbols. #### typeset CHARS CHARS=( a b c d e f g h i j k l m n o p q r s t u v w x y z \ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ 0 1 2 3 4 5 6 7 8 9 \; \: \. \~ \! \@ \# \$ \% \^ \& \* - + = \? ) typeset CNUM="${#CHARS[@]}" while getopts ":vVluadsnm#M#O#A#L#U#D#S#R#" OPTION do case "${OPTION}" in 'v') VERBOSE="${TRUE}";; 'V') VERYVERB="${TRUE}";; 'm') MINIMUM="${OPTARG}";; 'M') MAXIMUM="${OPTARG}";; 'O') MINOTHER="${OPTARG}";; 'A') MINALPHA="${OPTARG}";; 'L') MINLOWER="${OPTARG}";; 'U') MINUPPER="${OPTARG}";; 'D') MINDIGIT="${OPTARG}";; 'S') MINSYMBOL="${OPTARG}";; 'R') MAXREPEATS="${OPTARG}";; 'l') typeset -l CHARS=( "${CHARS[@]}" ) LONLY="${TRUE}" ;; 'u') typeset -u CHARS=( "${CHARS[@]}" ) UONLY="${TRUE}" ;; #### #### If the ALPHA ONLY option was selected on the command #### line, reset the array of valid characters to be a list #### of upper and lower case letters. #### 'a') AONLY="${TRUE}" CHARS=( a b c d e f g h i j k l m n o p q r s t u v w x y z \ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ) (( LONLY == TRUE )) && typeset -l CHARS=( "${CHARS[@]}" ) (( UONLY == TRUE )) && typeset -u CHARS=( "${CHARS[@]}" ) typeset CNUM="${#CHARS[@]}" ;; #### #### If the DIGITS ONLY option was selected on the command #### line, reset the array of valid characters to be a list #### of digits. #### 'd') DONLY="${TRUE}" CHARS=( 0 1 2 3 4 5 6 7 8 9 ) typeset CNUM="${#CHARS[@]}" ;; #### #### If the SYMBOLS ONLY option was selected on the command #### line, reset the array of valid characters to be a list #### of punctuation symbols. #### 's') SONLY="${TRUE}" CHARS=( \; \: \. \~ \! \@ \# \$ \% \^ \& \* - + = \? ) typeset CNUM="${#CHARS[@]}" ;; #### #### If ALPHANUMERIC option was selected on the command line, reset #### the array of valid characters to be upper and lower case #### letters, and digits. #### 'n') NONLY="${TRUE}" CHARS=( a b c d e f g h i j k l m n o p q r s t u v w x y z \ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ 0 1 2 3 4 5 6 7 8 9 ) (( LONLY == TRUE )) && typeset -l CHARS=( "${CHARS[@]}" ) (( UONLY == TRUE )) && typeset -u CHARS=( "${CHARS[@]}" ) typeset CNUM="${#CHARS[@]}" ;; '?') usagemsg_mkrandpwd_k93 "${0}" && return 1 ;; ':') usagemsg_mkrandpwd_k93 "${0}" && return 1 ;; '#') usagemsg_mkrandpwd_k93 "${0}" && return 1 ;; esac done shift $(( ${OPTIND} - 1 )) ################################################################ trap "usagemsg_mkrandpwd_k93 ${0}" EXIT #### Check to ensure the minimum number of characters allowed #### in the random password is less than or equal to the maximum. RETCODE="10" if ! (( MINIMUM <= MAXIMUM )) then print -u 2 "# ERROR: MINIMUM number of characters must be less than or equal to" print -u 2 "# MAXIMUM." return ${RETCODE} fi #### The minimum number of characters in the password must be #### greater than or equal to the minimum number of other #### characters plus the minimum number of alpha characters. (( ++RETCODE )) if ! (( MINIMUM >= ( MINOTHER + MINALPHA ) )) then print -u 2 "# ERROR: MINIMUM number of characters must be greater than or equal to" print -u 2 "# MINOTHER + MINALPHA." return ${RETCODE} fi #### The minimum number of characters in the password must be #### greater than or equal to the sum of the minimum number #### of lowercase letters, uppercase letters, digits, and #### symbols. (( ++RETCODE )) if ! (( MINIMUM >= ( MINLOWER + MINUPPER + MINDIGIT + MINSYMBOL ) )) then print -u 2 "# ERROR: MINIMUM number of characters must be greater than or equal to" print -u 2 "# MINLOWER + MINUPPER + MINDIGIT + MINSYMBOL." return ${RETCODE} fi #### The minimum number of characters in the password minus #### the minimum number of other characters must be greater #### than or equal to the sum of the minimum number of #### lowercase letters plus the minimum number of uppercase #### letters. (( ++RETCODE )) if ! (( ( MINIMUM - MINOTHER ) >= ( MINLOWER + MINUPPER ) )) then print -u 2 "# ERROR: MINIMUM minus MINOTHER characters must be greater than or equal to" print -u 2 "# MINLOWER + MINUPPER." return ${RETCODE} fi #### The minimum number of characters in the password minus #### the minimum number of alphabetic characters must be greater #### than or equal to the sum of the minimum number of #### digit characters plus the minimum number of symbol #### characters. (( ++RETCODE )) if ! (( ( MINIMUM - MINALPHA ) >= ( MINDIGIT + MINSYMBOL ) )) then print -u 2 "# ERROR: MINIMUM minus MINALPHA characters must be greater than or equal to" print -u 2 "# MINDIGIT + MINSYMBOL." return ${RETCODE} fi #### If the minimum number of alphabetic characters was #### provided on the command line, then the minimum number of #### alphabetic characters must be greater than or equal to #### the sum of the minimum number of lowercase characters #### plus the minimum number of uppercase characters. (( ++RETCODE )) if (( MINALPHA != 0 )) && ! (( MINALPHA >= ( MINLOWER + MINUPPER ) )) then print -u 2 "# ERROR: MINALPHA number of characters must be greater than or equal to" print -u 2 "# MINLOWER + MINUPPER." return ${RETCODE} fi #### If the minimum number of other characters was #### provided on the command line, then the minimum number of #### other characters must be greater than or equal to #### the sum of the minimum number of digits #### plus the minimum number of symbols. (( ++RETCODE )) if (( MINOTHER != 0 )) && ! (( MINOTHER >= ( MINDIGIT + MINSYMBOL ) )) then print -u 2 "# ERROR: MINOTHER number of characters must be greater than or equal to" print -u 2 "# MINDIGIT + MINSYMBOL." return ${RETCODE} fi #### If the alphabetic only option was selected on the #### command line, then the minimum number of other #### characters, the minimum number of digits, and the #### minimum number of symbols must all be zero (0). (( ++RETCODE )) if (( ( AONLY == TRUE ) && ( MINOTHER != 0 || MINDIGIT != 0 || MINSYMBOL != 0 ) )) then print -u 2 "# ERROR: ALPHA ONLY command line option was selected, therefore" print -u 2 "# MINOTHER, MINDIGIT, AND MINSYMBOL values must be zero." return ${RETCODE} fi #### If the alphanumeric only option was selected on the #### command line, then the minimum number of symbols #### must be zero (0). (( ++RETCODE )) if (( ( NONLY == TRUE ) && ( MINSYMBOL != 0 ) )) then print -u 2 "# ERROR: ALPHANUMERIC ONLY command line option was selected, therefore" print -u 2 "# MINSYMBOL value must be zero." return ${RETCODE} fi #### If the digit only option was selected on the command #### line, then the minimum number of other characters, the #### minimum number of alphabetic characters, and the minimum #### number of symbols must all be zero (0). (( ++RETCODE )) if (( ( DONLY == TRUE ) && ( MINALPHA != 0 || MINOTHER != 0 || MINSYMBOL != 0 ) )) then print -u 2 "# ERROR: DIGIT ONLY command line option was selected, therefore" print -u 2 "# MINALPHA, MINOTHER, AND MINSYMBOL values must be zero." return ${RETCODE} fi #### If the symbol only option was selected on the command #### line, then the minimum number of alphabetic characters, #### the minimum number of other characters, and the minimum #### number of symbols must all be zero (0). (( ++RETCODE )) if (( ( SONLY == TRUE ) && ( MINALPHA != 0 || MINOTHER != 0 || MINDIGIT != 0 ) )) then print -u 2 "# ERROR: SYMBOL ONLY command line option was selected, therefore" print -u 2 "# MINALPHA, MINOTHER, AND MINDIGIT values must be zero." return ${RETCODE} fi #### If the convert to lowercase letters option was selected #### on the command line, then the minimum number of #### uppercase letters must be zero (0). (( ++RETCODE )) if (( ( LONLY == TRUE ) && ( MINUPPER != 0 ) )) then print -u 2 "# ERROR: Convert to LOWERCASE command line option was selected, therefore" print -u 2 "# MINUPPER value must be zero." return ${RETCODE} fi #### If the convert to uppercase letters option was selected #### on the command line, then the minimum number of #### lowercase letters must be zero (0). (( ++RETCODE )) if (( ( UONLY == TRUE ) && ( MINLOWER != 0 ) )) then print -u 2 "# ERROR: Convert to UPPERCASE command line option was selected, therefore" print -u 2 "# MINLOWER value must be zero." return ${RETCODE} fi #### If the alphabetic only option was selected on the #### command line, then the alphanumeric, digit, and symbol #### only options must not be selected. (( ++RETCODE )) if (( AONLY == TRUE && ( NONLY == TRUE || DONLY == TRUE || SONLY == TRUE ) )) then print -u 2 "# ERROR: ALPHA ONLY command line option was selected, therefore" print -u 2 "# ALPHANUMERIC, DIGIT, and SYMBOL ONLY options must not be selected." return ${RETCODE} fi #### If the alphanumeric only option was selected on the #### command line, then the alphabetic, digit, and symbol #### only options must not be selected. (( ++RETCODE )) if (( NONLY == TRUE && ( DONLY == TRUE || SONLY == TRUE || AONLY == TRUE ) )) then print -u 2 "# ERROR: ALPHANUMERIC ONLY command line option was selected, therefore" print -u 2 "# ALPHA, DIGIT, and SYMBOL ONLY options must not be selected." return ${RETCODE} fi #### If the digit only option was selected on the command #### line, then the alphabetic, alphanumeric, and symbol only #### options must not be selected. (( ++RETCODE )) if (( DONLY == TRUE && ( SONLY == TRUE || AONLY == TRUE || NONLY == TRUE ) )) then print -u 2 "# ERROR: DIGIT ONLY command line option was selected, therefore" print -u 2 "# ALPHA, ALPHANUMERIC, and SYMBOL ONLY options must not be selected." return ${RETCODE} fi #### If the symbol only option was selected on the command #### line, then the alphabetic, alphanumeric, and digit only #### options must not be selected. (( ++RETCODE )) if (( SONLY == TRUE && ( AONLY == TRUE || NONLY == TRUE || DONLY == TRUE ) )) then print -u 2 "# ERROR: SYMBOL ONLY command line option was selected, therefore" print -u 2 "# ALPHA, ALPHANUMERIC, and DIGIT only options must not be selected." return ${RETCODE} fi RETCODE="0" trap "-" EXIT ################################################################ (( VERYVERB == TRUE )) && set -x (( VERBOSE == TRUE )) && print -u 2 -- "# Min length of random password string: ${MINIMUM}" (( VERBOSE == TRUE )) && print -u 2 -- "# Max length of random password string: ${MAXIMUM}" #### #### Calculate a random length for the random password string between the #### MINIMUM and MAXIMUM values provided #### (( MAXIMUM != MINIMUM )) && (( MAXIMUM++ )) (( MINIMUM == MAXIMUM )) && MAXLEN=${MINIMUM} || MAXLEN=$(( MINIMUM + ( RANDOM % ( MAXIMUM - MINIMUM ) ) )) (( VERBOSE == TRUE )) && print -u 2 -- "# Actual length of random password string: ${MAXLEN}" #### Test the randomly generated password to see if it #### conforms to the security requirements provided by #### default or from options provided on the command line. #### If not, regenerate a new password and retest. while (( OTHER < MINOTHER )) || (( ALPHA < MINALPHA )) || (( LOWER < MINLOWER )) || (( UPPER < MINUPPER )) || (( DIGIT < MINDIGIT )) || (( SYMBOL < MINSYMBOL )) || (( REPEATS > ( MAXREPEATS + 1 ) )) do (( VERBOSE == TRUE )) && print -u 2 -- "#\n# Iteration: $(( ++ITERATION ))" #### #### extract a random character from the CHARS array for each position #### of the random password string. The length of the random string #### determined by the MAXLEN variable. #### RANDSTR="" for (( POSCNT=0; POSCNT < MAXLEN; ++POSCNT )) do #### #### Using the modulo of a random number divided by the number of #### characters (array elements) in the CHARS array, provides a #### random array element position. The random array element position #### is used to append a single character at a time from the CHARS array #### onto the random password string variable RANDSTR. #### RANDSTR="${RANDSTR}${CHARS[${RANDOM}%${CNUM}]}" done #### Determine the number of alphabetic characters in the password typeset AVAL="${RANDSTR//[[:digit:][:punct:]]/}" ALPHA="${#AVAL}" #### Determine the number of non-alphabetic characters in the password typeset OVAL="${RANDSTR//[[:lower:][:upper:]]/}" OTHER="${#OVAL}" #### Determine the number of alphanumeric characters in the password typeset NVAL="${RANDSTR//[[:punct:]]/}" ALNUM="${#NVAL}" #### Determine the number of lowercase letters in the password typeset LVAL="${RANDSTR//[[:upper:][:digit:][:punct:]]/}" LOWER="${#LVAL}" #### Determine the number of uppercase letters in the password typeset UVAL="${RANDSTR//[[:lower:][:digit:][:punct:]]/}" UPPER="${#UVAL}" #### Determine the number of digits in the password typeset DVAL="${RANDSTR//[[:lower:][:upper:][:punct:]]/}" DIGIT="${#DVAL}" #### Determine the number of punctuation symbols in the password typeset SVAL="${RANDSTR//[[:lower:][:upper:][:digit:]]/}" SYMBOL="${#SVAL}" #### Determine how many times each character occurs in the password typeset REPEATS="-1" unset ARY typeset -A ARY for (( POSCNT=0; POSCNT < MAXLEN; ++POSCNT )) do (( ARY[${RANDSTR:POSCNT:1}] = ${ARY[${RANDSTR:POSCNT:1}]:-0} + 1 )) done #### Determine the maximum value of repeated characters in the password for IVAL in "${!ARY[@]}" do VAL="${ARY[${IVAL}]}" (( VAL > REPEATS )) && REPEATS="${VAL}" && RVAL="${IVAL}" done if (( VERBOSE == TRUE )) then print -u 2 -- "# Randomly generated password:|${RANDSTR}|" print -u 2 -- "# Min Alpha Chars: wanted at least ${MINALPHA}: got ${ALPHA}: ${AVAL}" print -u 2 -- "# Min Other Chars: wanted at least ${MINOTHER}: got ${OTHER}: ${OVAL}" print -u 2 -- "# Min Lower Chars: wanted at least ${MINLOWER}: got ${LOWER}: ${LVAL}" print -u 2 -- "# Min Upper Chars: wanted at least ${MINUPPER}: got ${UPPER}: ${UVAL}" print -u 2 -- "# Min Digit Chars: wanted at least ${MINDIGIT}: got ${DIGIT}: ${DVAL}" print -u 2 -- "# Min Symbol Chars: wanted at least ${MINSYMBOL}: got ${SYMBOL}: ${SVAL}" print -u 2 -- "# Max Repeat Chars: wanted at most ${MAXREPEATS}: got $(( REPEATS - 1)): ${RVAL} " fi done print -r -- "${RANDSTR}" return 0 } ################################################################ mkrandpwd_k93 "${@}"