randy (generador de passwords)
Nombre: randy
Autor: Wanako y Manuel Martín (quijote99)
Lenguaje: bash
Licencia: GPL
Propósito: Genera una password aleatoria (por defecto de 8 caraceteres) compuesta por caracteres alfanuméricos. Admite diversas opciones
Código:
#!/bin/bash
##############################
# randy: script para generar una contraseña aleatoria entre 1 y 30 caracteres.
# Longitud predeterminada 8 caracteres.
# Forma predeterminada alfanumérica minúsculas.
#
# Authors: quijote99 & wanako
# version: 1.4
# date: 09/04/2007
# encoding: UTF8
##############################
####### VARIABLES ############
NAME="${0##*/}" # $(basename $0)
MYNAME='randy'
LMIN='1' # length min
LMAX='30' # length max
LDEF='8' # length default
NL='\n' # newline
TB='\t' # tab
aPass=() # Array de passwords
NPASS='1' # Passwords generadas por defecto
np='0' # variable auxiliar para la generación de varias claves
SVER="$MYNAME versión 1.4 (09/04/2007)"
AUTHOR='Escrito por Manuel Martín (quijote99) y Wanako (guanaco?)'
##############################
####### FUNCTIONS ############
bd() { # bold color
echo -en "\E[1m$*\E[0m"
return
}
tb() { # tab tab
echo -en "$TB$TB"
return
}
nt() { # newline tab
echo -en "$NL$TB"
return
}
bb() { # bold color + background color
bd "\E[41m $* " # 41 is red
return
}
helpy() {
local PAGER OPTS='-[adlu|A][frs] -[L args] [-i args] [-n args]'
local HELP="$MYNAME(1)$(tb)${TB}User Manual$(tb)$TB$MYNAME(1)
$NL$(bd NOMBRE)
$TB$MYNAME - script para generar una contraseña aleatoria.
$NL$(bd SINOPSIS)$(nt)$(bd $MYNAME) [$(bd $OPTS)]
$TB$(bd $MYNAME) [$(bd OPCIONES)] [$(bd ARGUMENTOS)]
$NL$(bd DESCRIPCION)
${TB}Pequeña utilidad para generar claves aleatorias basándose en el algorit-
${TB}mo $(bd RANDOM) de Bash y en el dispositivo $(bd /dev/urandom).
$NL$(bd OPCIONES)
${TB}Este script utiliza la función $(bd getopts) de Bash y no admite opciones lar-
${TB}gas de la forma $(bd --option) como lo haría $(bd getopt), sin embargo algunas opcio
${TB}nes largas son predefinidas para emular este comportamiento y sólo son a
${TB}plicables como primer argumento pasado al script.
${TB}Si no se indicasen opciones la forma predeterminada de la clave es alfa-
${TB}numérica con letras minúsculas y una longitud de ocho caracteres.
$(nt)$(bd CORTAS)
$(nt)$(bd -a) (accents) acentos
$(tb)Genera la clave usando como patrón vocales acentuadas.
$(nt)$(bd -d) (digits) dígitos
$(tb)Genera la clave usando como patrón dígitos enteros positivos.
$(nt)$(bd -f) (force) forzar
$(tb)No solicita confirmación para generar la clave usando un patrón
$(tb)ingresado por el usuario, útil sólo en combinación con $(bd -m) (ver
$(tb)más abajo).
$(nt)$(bd -l) (lows) bajos?
$(tb)Genera la clave usando como patrón letras minúsculas del alfabe-
$(tb)to inglés incluidas las letras ñ y ü.
$(nt)$(bd -i \'args\') (include) caracteres
$(tb)ARGS es el patrón de caracteres que formará parte de la clave
$(tb)junto con otras opciones si existiesen, si no es alfanumérico se
$(tb)solicitará confirmación, debe ser entrecomillado para evitar la
$(tb)expansión de la shell y no contener espacios.
$(tb)Utilice esta opción con precaución, la combinación de caracteres
$(tb)que la shell interprete o expanda pueden dar resultados inespera
$(tb)dos e incluso provocar la pérdida de datos, de todas formas si
$(tb)el intérprete es Bash no debería ocurrir.
$(nt)$(bd -r) (repeat) repetir
$(tb)Genera la clave sin repetir caracteres del patrón, la longitud
$(tb)de la clave es la longitud del patrón si no se utiliza la opción
$(tb)$(bd -L) (ver más abajo) y si no supera el máximo permitido de 30.
$(nt)$(bd -s) (silent) silencio
$(tb)Imprime en pantalla sólo la clave generada y sin usar resalto de
$(tb)color, útil para redirigir o concatenar la salida hacia un fiche
$(tb)ro o hacia la entrada de otra aplicación.
$(nt)$(bd -u) (uppers) altos?
$(tb)Genera la clave usando como patrón letras mayúsculas del alfabe-
$(tb)to inglés incluidas las letras Ñ y Ü.
$(nt)$(bd -A) (All) todo
$(tb)Implica las opciones $(bd -adlu) (ver más arriba).
$(nt)$(bd -L args) (Length) Longitud
$(tb)ARGS es un dígito entero positivo que indica la longitud de la
$(tb)clave y debe ser mayor igual a 1 o menor igual a 30.
$(nt)$(bd -n arg) (n repeats) n contraseñas
$(tb)Genera \"n\" claves listadas en una columna. Las claves pueden
$(tb)aparecer repetidas
$(nt)$(bd LARGAS)
$(nt)$(bd --author) (autor)
$(tb)Imprime el autor y finaliza.
$(nt)$(bd --help) (ayuda)
$(tb)Imprime esta ayuda y finaliza.
$(nt)$(bd --version) (versión)
$(tb)Imprime la versión y finaliza.
$NL$(bd EJEMPLOS)
${TB}Los ejemplos siguientes son equivalentes:
$(tb)$MYNAME$(tb)${TB}(predeterminado)
$(tb)$MYNAME -ldL8$(tb)(similar)
$(tb)$MYNAME -l -d -L8$(tb)(similar)
$(tb)$MYNAME -l -d -L '8'${TB}(similar)
$(nt)Los ejemplos siguientes generan una clave con caracteres ingresados por
${TB}el usuario:
$(tb)$MYNAME -d -i '#'$(tb)(pregunta para utilizar '#')
$(tb)$MYNAME -df -i'#'$(tb)(no pregunta para utilizar '#')
$NL$(bd AUTOR)
$TB$AUTHOR
$NL$(bd BUGS)
${TB}Comunicar bichos a <museo@esdebian.org> :)
$NL$MYNAME(1)$(tb)$SVER$(tb)$TB$MYNAME(1)"
if PAGER="$(which less)" ; then
PAGER+=' -R'
( echo -e "$HELP" | $PAGER ) # ( subshell )
elif PAGER="$(which more)" ; then
PAGER+=' -f'
( echo -e "$HELP" | $PAGER )
else echo -e "$HELP" # auch, no pager
fi
exit 0
}
escape() {
echo -n "${*//\\/\\\\}" # \ -> \\
return
}
error() {
echo -e "$MYNAME: $*$NL$MYNAME: --help para recibir ayuda." > /dev/stderr
exit 1
}
warn() {
echo "$MYNAME: $*" > /dev/stderr
return
}
isdigit() {
if [[ "$@" =~ '[^[:digit:]]' ]] ; then
return 1
else return 0
fi
}
separate() {
local CHARS='' # default IFS=space for read then
while read -rsn 1 ; do # the space deposited by the user
CHARS+="$REPLY " # discarded, more safety?
done <<< "$@" # here string structure
echo "$CHARS"
return
}
digits() {
local DIGITS=()
DIGITS=( {0..9} )
echo "${DIGITS[*]}" # echo entire array - string
return
}
lowers() {
local LOWERS=()
LOWERS=( {a..z} ñ ü )
echo "${LOWERS[*]}"
return
}
uppers() {
local UPPERS=()
UPPERS=( {A..Z} Ñ Ü )
echo "${UPPERS[*]}"
return
}
accents() {
local ACCENTS=()
ACCENTS=( á é í ó ú )
echo "${ACCENTS[*]}"
return
}
default() {
local DEFAULT=()
DEFAULT=( $(digits) $(lowers) )
echo "${DEFAULT[*]}"
return
}
chkoptions() {
OPTIND=1 # to be sure that index is initialized to 1 each time
local OPT OPTIONS FORCE # it is called to getopts
while getopts ":aAdflL:i:rsun:" OPT ; do
case "$OPT" in
a|A|d|f|l|L|i|r|s|u|n )
if [[ "$OPTIONS" =~ ".*$OPT.*" ]] ; then
error "se espera sólo una ocurrencia de $(bd -$OPT)"
fi
OPTIONS+="$OPT" # OPTIONS="$OPTIONS$OPT"
case "$OPT" in
a|d|l|u )
if [[ "$OPTIONS" =~ 'A' ]] ; then
error "la opción $(bd -A) implica $(bd -adlu)"
fi
;;
A ) if [[ "$OPTIONS" =~ '(a|d|l|u)' ]] ; then
error "la opción $(bd -A) implica $(bd -adlu)"
fi
;;
esac
;;
* ) if [[ "$OPT" == ':' ]] ; then
error "se espera un argumento para $(bd -$OPTARG)"
else error "opción desconocida $(bd -$(escape $OPTARG))"
fi
;;
esac
done
if [[ "$OPTIND" -le "${#@}" ]] ; then
error "error de sintáxis $(bd $(escape ${@:$OPTIND:1}))"
fi
OPTIND=1 # to be sure again
while getopts ":aAdflL:i:rsun:" OPT ; do
if [[ "$OPT" == 'L' ]] ; then
if ( ! isdigit "$OPTARG" ) ; then
error "argumento no válido -L $(bd $(escape $OPTARG))"
elif [[ "$OPTARG" -lt "$LMIN" || "$OPTARG" -gt "$LMAX" ]] ; then
error "longitud fuera de rango $(bd $OPTARG)"
fi
fi
if [[ "$OPT" == 'n' ]] ; then
if ( ! isdigit "$OPTARG" ) ; then
error "argumento no válido -n $(bd $(escape $OPTARG))"
fi
fi
done
OPTIND=1 # to be sure again II
while getopts ":aAdflL:i:rsun:" OPT ; do
case "$OPT" in
f ) FORCE=true ;;
L ) LUSER="$OPTARG" ;; # global
r ) REPEAT=true ;; # global
s ) SILENT=true ;; # global
n ) NPASS="$OPTARG";; # global
esac
done
OPTIND=1 # to be sure again III
while getopts ":aAdflL:i:rsun:" OPT ; do
if [[ "$OPT" == 'i' ]] ; then
PUSER="${OPTARG//[[:space:]]/}" # global
if [[ ! "$FORCE" ]] ; then
if [[ "$OPTARG" =~ '([^[:alnum:]])' ]] ; then
OPTARG="${PUSER//[[:alnum:]]/}"
echo -n "$MYNAME: utilizar $(bd $(escape $OPTARG)) ? [s/n]"
read -sn 1
if [[ "$REPLY" =~ '(s|S)' ]] ; then
echo -e "$NL\E[A\E[M\E[A" # newline-up-clearline-up
else
echo -e "$NL\E[A\E[M\E[A"
echo "$MYNAME: cancelado..."
exit 0
fi
fi
fi
fi
done
return
}
genpattern() {
OPTIND=1 # to be sure again IV
local OPT PATTERN=()
while getopts ":aAdflL:i:rsu" OPT ; do
case "$OPT" in
a ) PATTERN+=( $(accents) ) ;;
A ) PATTERN=( $(default) $(uppers) $(accents) ) ;;
d ) PATTERN+=( $(digits) ) ;;
l ) PATTERN+=( $(lowers) ) ;;
i ) PATTERN+=( $(separate $PUSER) ) ;;
u ) PATTERN+=( $(uppers) ) ;;
esac
done
if [[ ! "${PATTERN[0]}" ]] ; then
PATTERN=( $(default) )
fi
echo "${PATTERN[*]}"
return
}
##############################
####### MAIN #################
set -f # no globbing
if [[ "$NAME" != "$MYNAME" ]] ; then # my name is randy :)
echo -e "$NAME ?${NL}Mi nombre es $(bd randy) !"
exit 1
fi
if [[ ! "$1" ]] ; then
LENGTH="$LDEF"
PATTERN=( $(default) )
elif [[ "$1" == '--help' ]] ; then
helpy
elif [[ "$1" == '--version' ]] ; then
echo "$SVER"
exit 0
elif [[ "$1" == '--author' ]] ; then
echo "$AUTHOR"
exit 0
elif [[ "$1" =~ '^[[.-.]][[:alpha:]]' ]] ; then
chkoptions "$@"
PATTERN=( $(genpattern $@) )
LENGTH="${LUSER:-$LDEF}" # LENGTH=($LUSER || $LDEF)
else error "error de sintáxis $(bd $(escape $1))"
fi
LPTN="${#PATTERN[@]}"
if [ "$LPTN" -eq '1' -a "$LENGTH" -eq '1' ] ; then
warn "nada que hacer con $(bd $(escape ${PATTERN[*]}))"
fi
if [[ "$REPEAT" ]] ; then
if [[ "$LUSER" ]] ; then
if [[ "$LUSER" -gt "$LPTN" ]] ; then
error "longitud de clave $(bd $LUSER) longitud de patrón $(bd $LPTN)"
fi
else
LENGTH="$LPTN"
if [[ "$LENGTH" -gt "$LMAX" ]] ; then
warn "longitud de clave $(bd $LENGTH) se trunca a $(bd $LMAX)"
LENGTH="$LMAX"
elif [[ "$LPTN" -eq '1' ]] ; then
warn "nada que hacer con $(bd $(escape ${PATTERN[*]}))"
fi
fi
fi
while [[ "$np" -lt "$NPASS" ]]; do
(( BYTES = LENGTH * 2 ))
SEEDS=( $(od -A n -d -N $BYTES < /dev/urandom) )
PASSWORD=''
for (( i=0 ; i < LENGTH ; i++ )) ; do
RANDOM="${SEEDS[$i]}"
PTNIND="$(($RANDOM%$LPTN))"
if [[ "$REPEAT" ]] ; then
PASSWORD+="${PATTERN[$PTNIND]}"
unset PATTERN[PTNIND]
PATTERN=( ${PATTERN[@]} )
LPTN="${#PATTERN[@]}"
else PASSWORD+="${PATTERN[$PTNIND]}"
fi
done
aPass[np]="${PASSWORD}"
let np++
done
for (( i=0 ; i < np ; i++ )); do
if [[ "$SILENT" ]] ; then
echo "${aPass[$i]}"
else echo "Contraseña: $(bb $(escape "${aPass[$i]}"))"
fi
done
exit 0
Comentarios: Se genera una clave aleatoria entre 1 y 30 caracteres, el valor por defecto son 8. Se permiten las siguientes opciones:
-a (accents) Genera la clave con vocales acentuadas [ á é í ó ú ]
-d (digits) Genera la clave con dígitos [0..9]
-l (lows) Genera la clave con letras minúsculas [a..z] incluida la ñ
-u (uppers) Genera la clave con letras mayúsculas [A..Z] incluida la Ñ
-A (All) Implica las opciones adlu
-L long Donde long indica la longitud de la password (8 - 30)
-i "lista" Si no se indicasen mas opciones que "-i" la password es generada sólo a partir de la lista. Pueden indicarse caracteres no alfanuméricos en cuyo caso se pedirá confirmación para generar la password. En este caso deben ser indicados entre comillas para no ser capturados por la shell.
-r No repite los caracteres de la lista o patrón indicado por -i
-s Modo silencioso, útil para utilizar junto con otros scipts
-n Se generan "n" contraseñas, un por línea