[prev in list] [next in list] [prev in thread] [next in thread]
List: jabber-jadmin
Subject: [jadmin] Support for MD5 on mod_auth_crypt
From: Camilo Martin Culpian <camiloculpian () gmail ! com>
Date: 2013-05-08 17:50:26
Message-ID: 518A9062.2080609 () gmail ! com
[Download RAW message or body]
Y add support for MD5 to mod_auth_crypt, if there is any one interested
the code is attached...
The full jabberd code compiles and run on debian6 (squeeze)...
The same way i can add support for sha256, sha512, etc...
(Sorry for my bad inglish)
["mod_auth_crypt.cc" (text/x-c++src)]
/*
* Copyrights
*
* Portions created by or assigned to Jabber.com, Inc. are
* Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
* information for Jabber.com, Inc. is available at http://www.jabber.com/.
*
* Portions Copyright (c) 1998-1999 Jeremie Miller.
*
* Portions Copyright (c) 2006-2007 Matthias Wimmer
*
* This file is part of jabberd14.
*
* This software 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 2 of the
* License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#include "jsm.h"
/**
* @file mod_auth_crypt.cc
* @brief handle (non-SASL) authentication using plain text passwords on the wire but \
hashes in storage
*
* This is an alternative implementation for plain text password on the wire (the \
other is mod_auth_crypt.c).
* The advantage of this module is that there are no plaintext passwords in the xdb \
storage, the advantage
* of mod_auth_crypt.c and using plain text passwords in the storage is, that other \
authentication
* schemes using hashes on the wire can be used.
*
* In general using mod_auth_crypt.c should be prefered. You will get into problems \
upgrading to harder
* authentication mechanisms if you use mod_auth_crypt.c.
*/
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif
#include <unistd.h>
#include <gcrypt.h>
#ifdef INCLUDE_CRYPT_H
# include <crypt.h>
#endif
#define HASH_CRYPT 1
#define HASH_SHA1 2
#define HASH_MD5 3
/**
* this function hashes the given password with the MD5 and formats the
* result to be usable for password storage
*
* @param password the password
* @param buf buffer where the result can be stored
* @param buflen length of the buffer (must be at least 32 bytes)
* @return 1 on success, 0 otherwise
*/
static int mod_auth_crypt_md5(char *password, char *buf, size_t buflen) {
/* our result is 32 characters long and we need a terminating '\0' */
if (buflen < (diglen * 2) + 1)
return 0;
/* the pointers have to be valid */
if (password == NULL || buf == NULL)
return 0;
/* calculate the hash */
unsigned int diglen = gcry_md_get_algo_dlen (GCRY_MD_MD5);
gcry_md_hd_t md5 = NULL;
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
gcry_md_open (&md5, GCRY_MD_MD5, GCRY_MD_FLAG_SECURE);
gcry_md_write (md5, password, strlen(password));
gcry_md_final (md5);
unsigned char *digest = gcry_md_read (md5, GCRY_MD_MD5);
unsigned int i;
for (i = 0; i < diglen; i++) {
sprintf(buf + (i * 2), "%02x", digest[i]);
}
buf[diglen * 2] = '\0';
gcry_md_close(md5);
return 1;
}
/**
* this function hashes the given password with the SHA1 and formats the
* result to be usable for password storage
*
* @param password the password
* @param buf buffer where the result can be stored
* @param buflen length of the buffer (must be at least 32 bytes)
* @return 1 on success, 0 otherwise
*/
static int mod_auth_crypt_sha1(char *password, char *buf, size_t buflen) {
unsigned char hash[20];
/* our result is 34 characters long and we need a terminating '\0' */
if (buflen < 35)
return 0;
/* the pointers have to be valid */
if (password == NULL || buf == NULL)
return 0;
/* calculate the hash */
shaBlock((unsigned char *)password, j_strlen(password), hash);
/* write the result */
return base64_encode(hash, sizeof(hash), buf, buflen);
}
/**
* handle authentication requests
*
* get requests are used to check which authentication methods are available,
* set requests are the actual authentication requests
*
* @param m the mapi_struct containing the request
* @param arg unused/ignored
* @return M_HANDLED if the request has been completely handled, M_PASS else (other \
modules get the chance to handle it)
*/
static mreturn mod_auth_crypt_jane(mapi m, void *arg) {
char shahash[35];
char* hashalgo;
int usedhashalgo;
char *passA, *passB;
char salt[3];
xmlnode xdb;
xmlnode mod_auth_crypt_config = js_config(m->si, "jsm:mod_auth_crypt", NULL);
log_debug2(ZONE, LOGT_AUTH, "checking");
if(jpacket_subtype(m->packet) == JPACKET__GET) {
/* type=get means we flag that the server can do plain-text auth */
xmlnode_insert_tag_ns(m->packet->iq, "password", NULL, NS_AUTH);
return M_PASS;
}
if((passA = xmlnode_get_data(xmlnode_get_list_item(xmlnode_get_tags(m->packet->iq, \
"auth:password", m->si->std_namespace_prefixes), 0))) == NULL) return M_PASS;
/* make sure we can get the auth packet and that it contains a password */
xdb = xdb_get(m->si->xc, m->user->id, NS_AUTH_CRYPT);
if (xdb == NULL || (passB = xmlnode_get_data(xdb)) == NULL) {
xmlnode_free(xdb);
return M_PASS;
}
hashalgo = xmlnode_get_data(xmlnode_get_list_item(xmlnode_get_tags(mod_auth_crypt_config, \
"jsm:hash", m->si->std_namespace_prefixes), 0));
if (j_strcasecmp(hashalgo, "SHA1") == 0) {
usedhashalgo = HASH_SHA1;
} else if (j_strcasecmp(hashalgo, "MD5") == 0){
usedhashalgo = HASH_MD5;
}else{
usedhashalgo = HASH_CRYPT;
}
switch (usedhashalgo) {
case HASH_SHA1:
mod_auth_crypt_sha1(passA, shahash, sizeof(shahash));
passA = shahash;
log_debug2(ZONE, LOGT_AUTH, "comparing %s %s",shahash,passB);
break;
case HASH_MD5:
mod_auth_crypt_md5(passA, shahash, sizeof(shahash));
passA = shahash;
log_debug2(ZONE, LOGT_AUTH, "comparing %s %s",shahash,passB);
break;
default:
strncpy(salt, passB, 2);
salt[2] = '\0';
passA = crypt(passA, salt);
log_debug2(ZONE, LOGT_AUTH, "comparing %s %s",passA,passB);
}
if(strcmp(passA, passB) != 0)
jutil_error_xmpp(m->packet->x, XTERROR_AUTH);
else
jutil_iqresult(m->packet->x);
xmlnode_free(xdb); /* free xdb results */
return M_HANDLED;
}
/**
* get a random salt
*
* @note this is not thread safe. Calls overwrite the result of previous calls.
*
* @return pointer to a two character string containing the new salt
*/
static char* mod_auth_crypt_get_salt() {
static char result[3] = { '\0', '\0', '\0'};
int i = 0;
if (!result[0])
srand(time(NULL));
for (i = 0; i < 2; i++) {
result[i] = (char)(rand() % 64) + '.';
if (result[i] <= '9')
continue;
result[i] += 'A' - '9' - 1;
if (result[i] <= 'Z')
continue;
result[i] += 'a' - 'Z' - 1;
}
return result;
}
/**
* store a new password (hash of it) in the xdb storage
*
* @param m the mapi_struct containing the request, that is related to the password \
update
* @param id for which user the password should be updated
* @param pass the new password for the user
* @return 0 on success, 1 if updated failed
*/
static int mod_auth_crypt_reset(mapi m, jid id, xmlnode pass) {
char shahash[35];
char* password;
xmlnode newpass;
char* hashalgo;
int usedhashalgo;
xmlnode mod_auth_crypt_config = js_config(m->si, "jsm:mod_auth_crypt", NULL);
log_debug2(ZONE, LOGT_AUTH, "resetting password");
hashalgo = xmlnode_get_data(xmlnode_get_list_item(xmlnode_get_tags(mod_auth_crypt_config, \
"jsm:hash", m->si->std_namespace_prefixes), 0)); if (j_strcasecmp(hashalgo, "SHA1") \
== 0) { usedhashalgo = HASH_SHA1;
} else if (j_strcasecmp(hashalgo, "MD5") == 0){
usedhashalgo = HASH_MD5;
} else {
usedhashalgo = HASH_CRYPT;
}
xmlnode_free(mod_auth_crypt_config);
mod_auth_crypt_config = NULL;
hashalgo = NULL;
password = xmlnode_get_data(pass);
if (password == NULL)
return 1;
newpass = xmlnode_new_tag_ns("crypt", NULL, NS_AUTH_CRYPT);
switch (usedhashalgo) {
case HASH_SHA1:
mod_auth_crypt_sha1(password, shahash, sizeof(shahash));
log_debug2(ZONE, LOGT_AUTH, "SHA1 hash is %s", shahash);
if (xmlnode_insert_cdata(newpass, shahash, -1) == NULL)
return -1;
break;
case HASH_MD5:
mod_auth_crypt_md5(password, shahash, sizeof(shahash));
log_debug2(ZONE, LOGT_AUTH, "MD5 hash is %s", shahash);
if (xmlnode_insert_cdata(newpass, shahash, -1) == NULL)
return -1;
break;
default:
if (xmlnode_insert_cdata(newpass, crypt(password, mod_auth_crypt_get_salt()), \
-1) == NULL) return -1;
}
return xdb_set(m->si->xc, jid_user(id), NS_AUTH_CRYPT, newpass);
}
/**
* handle request for required registration data
*
* used if the user just registers his account
*
* requests are used to check if authentication type is supported
*
* @param m the mapi instance containing the query
* @param arg type of action (password change or register request) for logging
* @return always M_PASS
*/
static mreturn mod_auth_crypt_reg(mapi m, void *arg) {
if (jpacket_subtype(m->packet) == JPACKET__GET) {
/* type=get means we tell what we need */
if (xmlnode_get_tags(m->packet->iq, "register:password", \
m->si->std_namespace_prefixes) == NULL) xmlnode_insert_tag_ns(m->packet->iq, \
"password", NULL, NS_REGISTER); }
return M_PASS;
}
/**
* handle saving the password
*
* used if the user just registers his account or if the user changed
* his password
*
* @param m the mapi instance containing the query
* @param arg unused/ignored
* @return M_HANDLED if we rejected the request, H_PASS else
*/
static mreturn mod_auth_crypt_pwchange(mapi m, void *arg) {
jid id;
xmlnode pass;
/* get the jid of the user */
id = jid_user(m->packet->to);
/* get the new password */
pass = xmlnode_get_list_item(xmlnode_get_tags(m->packet->iq, "auth:password", \
m->si->std_namespace_prefixes), 0);
/* tuck away for a rainy day */
if (mod_auth_crypt_reset(m, id, pass)) {
js_bounce_xmpp(m->si, m->s, m->packet->x, XTERROR_STORAGE_FAILED);
return M_HANDLED;
}
return M_PASS;
}
/**
* delete the password if a user is deleted
*
* @param m the mapi_struct
* @param arg unused/ignored
* @return always M_PASS
*/
static mreturn mod_auth_crypt_delete(mapi m, void *arg) {
xdb_set(m->si->xc, m->user->id, NS_AUTH, NULL); /* to be sure */
xdb_set(m->si->xc, m->user->id, NS_AUTH_CRYPT, NULL);
return M_PASS;
}
/**
* init the mod_auth_crypt module, register the callbacks with the Jabber session \
manager
*
* @param si the jsmi_struct containing session manager instance-internal data
*/
extern "C" void mod_auth_crypt(jsmi si) {
log_debug2(ZONE, LOGT_INIT, "init");
log_warn(NULL, "Aded MD5 Support by Willi Wonca.....");
xmlnode register_config = js_config(si, "register:register", NULL);
js_mapi_register(si, e_AUTH, mod_auth_crypt_jane, NULL);
js_mapi_register(si, e_PASSWORDCHANGE, mod_auth_crypt_pwchange, NULL);
if (register_config != NULL)
js_mapi_register(si, e_REGISTER, mod_auth_crypt_reg, NULL);
js_mapi_register(si, e_DELETE, mod_auth_crypt_delete, NULL);
xmlnode_free(register_config);
}
_______________________________________________
JAdmin mailing list
Info: http://mail.jabber.org/mailman/listinfo/jadmin
Unsubscribe: JAdmin-unsubscribe@jabber.org
_______________________________________________
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic