ircu2/ircd/umkpasswd.c

488 lines
12 KiB
C

/*
* IRC - Internet Relay Chat, ircd/umkpasswd.c
* Copyright (C) 2002 hikari
*
* 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; either version 1, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: umkpasswd.c 1842 2007-11-17 13:48:15Z entrope $
*/
#include "config.h"
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
/* #include <assert.h> -- Now using assert in ircd_log.h */
/* ircu headers */
#include "ircd_alloc.h"
#include "ircd_log.h" /* for ircd's assert.h */
#include "ircd_string.h"
#include "umkpasswd.h"
#include "s_debug.h"
#include "ircd_md5.h"
/* crypto mech headers */
#include "ircd_crypt.h"
#include "ircd_crypt_smd5.h"
#include "ircd_crypt_native.h"
#include "ircd_crypt_plain.h"
#include "ircd_crypt_bcrypt.h"
/* bleah, evil globals */
umkpasswd_conf_t* umkpasswd_conf;
crypt_mechs_t* crypt_mechs_root;
int log_inassert = 0;
time_t CurrentTime;
void sendto_opmask_butone(struct Client *one, unsigned int mask,
const char *pattern, ...)
{
/* only needed with memdebug, which also calls Debug() */
}
void copyright(void)
{
fprintf(stderr, "umkpasswd - Copyright (c) 2002 hikari\n");
return;
}
void show_help(void)
{
#ifdef DEBUGMODE
char *debughelp = "[-d <level>] ";
#else
char *debughelp = "";
#endif
copyright();
/*fprintf(stderr, "umkpasswd [-l] [[[-a]||[-u]] <username>] [-y <class>] %s[-c <file>] -m <mech> [password]\n\n", debughelp);*/
fprintf(stderr, "umkpasswd [-l] %s-m <mech> [password]\n\n", debughelp);
fprintf(stderr, " -l List mechanisms available.\n");
#if 0
fprintf(stderr, " -a <user> Add user to conf file.\n");
fprintf(stderr, " -u <user> Update user's password field.\n");
fprintf(stderr, " -y <class> Class to place oper in.\n");
#endif
fprintf(stderr, " -m <mech> Mechanism to use [MANDATORY].\n");
#ifdef DEBUGMODE
fprintf(stderr, " -d <level> Debug level to run at.\n");
#endif
/*
fprintf(stderr, " -c <file> Conf file to use, default is DPATH/CPATH.\n\n");
*/
return;
}
/* our implementation of debug() */
void debug(int level, const char *form, ...)
{
va_list vl;
int err = errno;
if (level <= (umkpasswd_conf->debuglevel))
{
va_start(vl, form);
vfprintf(stderr, form, vl);
fprintf(stderr, "\n");
va_end(vl);
}
errno = err;
}
/* quick implementation of log_write() for assert() call */
void log_write(enum LogSys subsys, enum LogLevel severity,
unsigned int flags, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
vfprintf(stderr, fmt, vl);
fprintf(stderr, "\n");
va_end(vl);
}
/** Generate random bytes from /dev/urandom
* @param buf Buffer to fill
* @param len Number of bytes to generate
* @return 0 on success, -1 on failure
*/
static int get_random_bytes(unsigned char* buf, size_t len)
{
int fd;
ssize_t n;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return -1;
n = read(fd, buf, len);
close(fd);
return (n == (ssize_t)len) ? 0 : -1;
}
/* secure salt generator using /dev/urandom */
char *make_salt(const char *salts)
{
char *tmp = NULL;
unsigned char rand_bytes[2];
size_t salts_len;
salts_len = strlen(salts);
if((tmp = calloc(3, sizeof(char))) != NULL)
{
/* Use /dev/urandom for cryptographically secure random salt */
if (get_random_bytes(rand_bytes, 2) < 0) {
/* Fallback to less secure method if /dev/urandom unavailable */
rand_bytes[0] = (unsigned char)(time(NULL) & 0xFF);
rand_bytes[1] = (unsigned char)((time(NULL) >> 8) & 0xFF);
}
tmp[0] = salts[rand_bytes[0] % salts_len];
tmp[1] = salts[rand_bytes[1] % salts_len];
Debug((DEBUG_DEBUG, "salts = %s", salts));
Debug((DEBUG_DEBUG, "strlen(salts) = %d", salts_len));
}
return tmp;
}
/* our implementation of ircd_crypt_register_mech() */
int ircd_crypt_register_mech(crypt_mech_t* mechanism)
{
crypt_mechs_t* crypt_mech;
Debug((DEBUG_INFO, "ircd_crypt_register_mech: registering mechanism: %s", mechanism->shortname));
/* try to allocate some memory for the new mechanism */
if ((crypt_mech = (crypt_mechs_t*)MyMalloc(sizeof(crypt_mechs_t))) == NULL)
{
/* aww poot, we couldn't get any memory, scream a little then back out */
Debug((DEBUG_MALLOC, "ircd_crypt_register_mech: could not allocate memory for %s", mechanism->shortname));
return -1;
}
/* ok, we have memory, initialise it */
memset(crypt_mech, 0, sizeof(crypt_mechs_t));
/* assign the data */
crypt_mech->mech = mechanism;
crypt_mech->next = crypt_mech->prev = NULL;
/* first of all, is there anything there already? */
if(crypt_mechs_root->next == NULL)
{
/* nope, just add ourself */
crypt_mechs_root->next = crypt_mechs_root->prev = crypt_mech;
} else {
/* nice and simple, put ourself at the end */
crypt_mech->prev = crypt_mechs_root->prev;
crypt_mech->next = NULL;
crypt_mechs_root->prev = crypt_mech->prev->next = crypt_mech;
}
/* we're done */
Debug((DEBUG_INFO, "ircd_crypt_register_mech: registered mechanism: %s, crypt_function is at 0x%X.", crypt_mech->mech->shortname, &crypt_mech->mech->crypt_function));
Debug((DEBUG_INFO, "ircd_crypt_register_mech: %s: %s", crypt_mech->mech->shortname, crypt_mech->mech->description));
return 0;
}
void sum(char* tmp)
{
char* str;
FILE* file;
MD5_CTX context;
int len;
unsigned char buffer[1024], digest[16], vstr[32];
vstr[0] = '\0';
str = tmp + strlen(tmp);
while (str[-1] == '\r' || str[-1] == '\n') *--str = '\0';
if (NULL == (file = fopen(tmp, "r")))
{
fprintf(stderr, "unable to open %s: %s", tmp, strerror(errno));
exit(0);
}
MD5Init(&context);
while ((fgets((char*)buffer, sizeof(buffer), file)) != NULL)
{
MD5Update(&context, buffer, strlen((char*)buffer));
str = strstr((char*)buffer, "$Id: ");
if (str != NULL)
{
for (str += 5; !isspace(*str); ++str) {}
while (isspace(*++str)) {}
for (len = 0; !isspace(str[len]); ++len) vstr[len] = str[len];
vstr[len] = '\0';
break;
}
}
while ((len = fread (buffer, 1, sizeof(buffer), file)))
MD5Update(&context, buffer, len);
MD5Final(digest, &context);
fclose(file);
str = strrchr(tmp, '/');
printf(" \"[ %s: ", str ? (str + 1) : tmp);
for (len = 0; len < 16; len++)
printf ("%02x", digest[len]);
printf(" %s ]\",\n", vstr);
}
/* dump the loaded mechs list */
void show_mechs(void)
{
crypt_mechs_t* mechs;
copyright();
printf("\nAvailable mechanisms:\n");
if(crypt_mechs_root == NULL)
return;
mechs = crypt_mechs_root->next;
for(;;)
{
if(mechs == NULL)
return;
printf(" %s\t\t%s\n", mechs->mech->mechname, mechs->mech->description);
mechs = mechs->next;
}
}
/* load in the mech "modules" */
void load_mechs(void)
{
/* we need these loaded by hand for now */
ircd_register_crypt_native();
ircd_register_crypt_smd5();
ircd_register_crypt_plain(); /* yes I know it's slightly pointless */
ircd_register_crypt_bcrypt();
return;
}
crypt_mechs_t* hunt_mech(const char* mechname)
{
crypt_mechs_t* mech;
assert(NULL != mechname);
if(crypt_mechs_root == NULL)
return NULL;
mech = crypt_mechs_root->next;
for(;;)
{
if(mech == NULL)
return NULL;
if(0 == (ircd_strcmp(mech->mech->mechname, mechname)))
return mech;
mech = mech->next;
}
}
char* crypt_pass(const char* pw, const char* mech)
{
crypt_mechs_t* crypt_mech;
char* salt, *untagged, *tagged;
int len;
assert(NULL != pw);
assert(NULL != mech);
Debug((DEBUG_DEBUG, "pw = %s\n", pw));
Debug((DEBUG_DEBUG, "mech = %s\n", mech));
if (NULL == (crypt_mech = hunt_mech(mech)))
{
printf("Unable to find mechanism %s\n", mech);
return NULL;
}
salt = make_salt(default_salts);
untagged = (char *)CryptFunc(crypt_mech->mech)(pw, salt);
tagged = (char *)MyMalloc(strlen(untagged)+CryptTokSize(crypt_mech->mech)+1);
len = strlen(untagged)+1;
memset(tagged, 0, strlen(untagged)+CryptTokSize(crypt_mech->mech)+1);
strncpy(tagged, CryptTok(crypt_mech->mech), CryptTokSize(crypt_mech->mech));
strncpy(tagged+CryptTokSize(crypt_mech->mech), untagged, len);
return tagged;
}
char* parse_arguments(int argc, char **argv)
{
int len = 0, c = 0;
const char* options = "a:d:lm:u:y:5";
umkpasswd_conf = (umkpasswd_conf_t*)MyMalloc(sizeof(umkpasswd_conf_t));
umkpasswd_conf->flags = 0;
umkpasswd_conf->debuglevel = 0;
umkpasswd_conf->operclass = 0;
umkpasswd_conf->user = NULL;
umkpasswd_conf->mech = NULL;
len = strlen(DPATH) + strlen(CPATH) + 2;
umkpasswd_conf->conf = (char *)MyMalloc(len*sizeof(char));
memset(umkpasswd_conf->conf, 0, len*sizeof(char));
ircd_strncpy(umkpasswd_conf->conf, DPATH, strlen(DPATH) + 1);
*((umkpasswd_conf->conf) + strlen(DPATH)) = '/';
ircd_strncpy((umkpasswd_conf->conf) + strlen(DPATH) + 1, CPATH, strlen(CPATH) + 1);
len = 0;
while ((EOF != (c = getopt(argc, argv, options))) && !len)
{
switch (c)
{
case '5':
{
char t1[1024];
while (fgets(t1, sizeof(t1), stdin)) sum(t1);
}
exit(0);
case 'y':
umkpasswd_conf->operclass = atoi(optarg);
if (umkpasswd_conf->operclass < 0)
umkpasswd_conf->operclass = 0;
break;
case 'u':
if(umkpasswd_conf->flags & ACT_ADDOPER)
{
fprintf(stderr, "-a and -u are mutually exclusive. Use either or neither.\n");
abort(); /* b0rk b0rk b0rk */
}
umkpasswd_conf->flags |= ACT_UPDOPER;
umkpasswd_conf->user = optarg;
break;
case 'm':
umkpasswd_conf->mech = optarg;
break;
case 'l':
load_mechs();
show_mechs();
exit(0);
break;
case 'd':
umkpasswd_conf->debuglevel = atoi(optarg);
if (umkpasswd_conf->debuglevel < 0)
umkpasswd_conf->debuglevel = 0;
break;
case 'c':
umkpasswd_conf->conf = optarg;
break;
case 'a':
if(umkpasswd_conf->flags & ACT_UPDOPER)
{
fprintf(stderr, "-a and -u are mutually exclusive. Use either or neither.\n");
abort(); /* b0rk b0rk b0rk */
}
umkpasswd_conf->flags |= ACT_ADDOPER;
umkpasswd_conf->user = optarg;
break;
default:
/* unknown option - spit out syntax and b0rk */
show_help();
exit(1);
break;
}
}
Debug((DEBUG_DEBUG, "flags = %d", umkpasswd_conf->flags));
Debug((DEBUG_DEBUG, "operclass = %d", umkpasswd_conf->operclass));
Debug((DEBUG_DEBUG, "debug = %d", umkpasswd_conf->debuglevel));
if (NULL != umkpasswd_conf->mech)
Debug((DEBUG_DEBUG, "mech = %s", umkpasswd_conf->mech));
else
Debug((DEBUG_DEBUG, "mech is unset"));
if (NULL != umkpasswd_conf->conf)
Debug((DEBUG_DEBUG, "conf = %s", umkpasswd_conf->conf));
else
Debug((DEBUG_DEBUG, "conf is unset"));
if (NULL != umkpasswd_conf->user)
Debug((DEBUG_DEBUG, "user = %s", umkpasswd_conf->user));
else
Debug((DEBUG_DEBUG, "user is unset"));
/* anything left over should be password */
return argv[optind];
}
int main(int argc, char **argv)
{
char* pw, *crypted_pw;
crypt_mechs_root = (crypt_mechs_t*)MyMalloc(sizeof(crypt_mechs_t));
crypt_mechs_root->mech = NULL;
crypt_mechs_root->next = crypt_mechs_root->prev = NULL;
if (argc < 2)
{
show_help();
exit(0);
}
pw = parse_arguments(argc, argv);
load_mechs();
if (NULL == umkpasswd_conf->mech)
{
fprintf(stderr, "No mechanism specified.\n");
abort();
}
if (NULL == pw)
{
pw = getpass("Password: ");
}
crypted_pw = crypt_pass(pw, umkpasswd_conf->mech);
printf("Crypted Pass: %s\n", crypted_pw);
memset(pw, 0, strlen(pw));
return 0;
}