Bordeaux is a voice application server for modern IP telephony networks using modern C++ practices along with advances originally made in GNU Bayonne.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

178 lines
4.7 KiB

/*
* Copyright (C) 2020-2021 David Sugar <tychosoft@gmail.com>.
*
* 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
*/
#include "compiler.hpp"
#include "print.hpp"
#include "strings.hpp"
#include "args.hpp"
#include <cstdlib>
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <termios.h>
#include <unistd.h>
namespace {
const char digits[17] = "0123456789abcdef";
const size_t sha256_len = 32;
const size_t md5_len = 16;
args::option opt_config('c', "--config", "config file path");
args::option opt_digest('d', "--digest", "algorithm to use", "sha256|md5");
args::option helpflag('h', "--help", "display this list");
args::option opt_passwd('p', "--password", "password to hash", "string");
args::option opt_realm('r', "--realm", "realm to encode for", "string");
args::option verbose('v', "--verbose", "full output line");
args::option althelp('?', nullptr, nullptr);
auto to_hex(const uint8_t *data, size_t size)
{
std::ostringstream out;
while(data && size--) {
out << digits[*data >> 4] << digits[*data & 0x0f];
++data;
}
return out.str();
}
auto sha256_secret(std::string_view userid, std::string_view realm, std::string_view secret)
{
SHA256_CTX ctx;
uint8_t buf[sha256_len];
SHA256_Init(&ctx);
SHA256_Update(&ctx, userid.data(), userid.size());
SHA256_Update(&ctx, ":", 1);
SHA256_Update(&ctx, realm.data(), realm.size());
SHA256_Update(&ctx, ":", 1);
SHA256_Update(&ctx, secret.data(), secret.size());
SHA256_Final(buf, &ctx);
return to_hex(buf, sizeof(buf));
}
auto md5_secret(std::string_view userid, std::string_view realm, std::string_view secret)
{
MD5_CTX ctx;
uint8_t buf[md5_len];
MD5_Init(&ctx);
MD5_Update(&ctx, userid.data(), userid.size());
MD5_Update(&ctx, ":", 1);
MD5_Update(&ctx, realm.data(), realm.size());
MD5_Update(&ctx, ":", 1);
MD5_Update(&ctx, secret.data(), secret.size());
MD5_Final(buf, &ctx);
return to_hex(buf, sizeof(buf));
}
void set_echo(bool enable = true)
{
struct termios tty{};
tcgetattr(STDIN_FILENO, &tty);
if(!enable)
tty.c_lflag &= ~ECHO;
else
tty.c_lflag |= ECHO;
(void) tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}
void get_realm()
{
static char buf[256];
if(!gethostname(buf, sizeof(buf) - 1)) {
buf[sizeof(buf) - 1] = 0;
opt_realm.set(buf);
}
else
throw bad_arg("no realm found or hostname set");
}
void get_pass()
{
set_echo(false);
std::cout << "password: ";
std::cout.flush();
std::string password;
std::cin >> password;
std::cout << std::endl;
std::cout << "verify: ";
std::cout.flush();
std::string verify;
std::cin >> verify;
set_echo(true);
std::cout << std::endl;
if(verify != password)
throw bad_arg("passwords do not match");
opt_passwd.set(password.c_str());
}
} // end namespace
auto main(int argc, const char **argv) -> int
{
int exit_code = 0;
atexit([]{
set_echo(true);
});
try {
auto count = args::parse(argc, argv);
if(is(helpflag) || is(althelp) || !count) {
args::help({"sip-hash [options] userid"}, {
"Compute hash secret for a sip user"
});
return 0;
}
auto userid = args::list()[0].data();
if(!is(opt_digest))
opt_digest.set("sha-256");
auto digest = lower_case(*opt_digest);
if(digest != "md5" && digest != "sha" && digest != "sha256" && digest != "sha-256") {
digest += ": unsupported algorithm";
throw bad_arg(digest.c_str());
}
if(!is(opt_realm))
get_realm();
if(!is(opt_passwd))
get_pass();
if(is(verbose))
print("{}:{}:", userid, *opt_realm);
if(digest == "md5")
print("{}\n", md5_secret(userid, *opt_realm, *opt_passwd));
else
print("{}\n", sha256_secret(userid, *opt_realm, *opt_passwd));
}
catch(const std::exception& e) {
die(-1, "*** sip-hash: {}\n", e.what());
}
catch(...) {
die(-1, "*** sip-hash: unknown error\n");
}
return exit_code;
}