| /* |
| * Copyright (c) 2011 Hunter Morris <hunter.morris@smarkets.com> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <erl_interface.h> |
| #include <ei.h> |
| #include <unistd.h> |
| |
| #include "erl_blf.h" |
| |
| #define dec_int16(s) ((((unsigned char*)(s))[0] << 8) | \ |
| (((unsigned char*)(s))[1])) |
| |
| #define enc_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \ |
| ((unsigned char*)(s))[1] = (i) & 0xff;} |
| |
| #define BUFSIZE (1 << 16) |
| #define CMD_SALT 0 |
| #define CMD_HASHPW 1 |
| |
| typedef unsigned char byte; |
| |
| char *bcrypt(char *, const char *, const char *); |
| void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t); |
| |
| /* These methods came from the Erlang port command tutorial: |
| * http://www.erlang.org/doc/tutorial/c_port.html#4.2 |
| */ |
| static int |
| read_buf(int fd, byte *buf, int len) |
| { |
| int i, got = 0; |
| do { |
| if ((i = read(fd, buf+got, len-got)) <= 0) { |
| if (i == 0) return got; |
| if (errno != EINTR) |
| return got; |
| i = 0; |
| } |
| got += i; |
| } while (got < len); |
| return (len); |
| } |
| |
| static int |
| read_cmd(byte *buf) |
| { |
| int len; |
| if (read_buf(0, buf, 2) != 2) |
| return 0; |
| len = dec_int16(buf); |
| if (read_buf(0, buf, len) != len) |
| return 0; |
| return 1; |
| } |
| |
| static int |
| write_buf(int fd, byte *buf, int len) |
| { |
| int i, done = 0; |
| do { |
| if ((i = write(fd, buf+done, len-done)) < 0) { |
| if (errno != EINTR) |
| return (i); |
| i = 0; |
| } |
| done += i; |
| } while (done < len); |
| return (len); |
| } |
| |
| static int |
| write_cmd(byte *buf, int len) |
| { |
| byte hd[2]; |
| enc_int16(len, hd); |
| if (write_buf(1, hd, 2) != 2) |
| return 0; |
| if (write_buf(1, buf, len) != len) |
| return 0; |
| return 1; |
| } |
| |
| static int |
| process_reply(ETERM *pid, int cmd, const char *res) |
| { |
| ETERM *result; |
| int len, retval; |
| byte *buf; |
| result = erl_format("{~i, ~w, ~s}", cmd, pid, res); |
| len = erl_term_len(result); |
| buf = erl_malloc(len); |
| erl_encode(result, buf); |
| retval = write_cmd(buf, len); |
| erl_free_term(result); |
| erl_free(buf); |
| return retval; |
| } |
| |
| static int |
| process_encode_salt(ETERM *pid, ETERM *data) |
| { |
| int retval = 0; |
| ETERM *pattern, *cslt, *lr; |
| byte *csalt = NULL; |
| long log_rounds = -1; |
| int csaltlen = -1; |
| char ret[64]; |
| pattern = erl_format("{Csalt, LogRounds}"); |
| if (erl_match(pattern, data)) { |
| cslt = erl_var_content(pattern, "Csalt"); |
| csaltlen = ERL_BIN_SIZE(cslt); |
| csalt = ERL_BIN_PTR(cslt); |
| lr = erl_var_content(pattern, "LogRounds"); |
| log_rounds = ERL_INT_UVALUE(lr); |
| if (16 != csaltlen) { |
| retval = process_reply(pid, CMD_SALT, "Invalid salt length"); |
| } else if (log_rounds < 4 || log_rounds > 31) { |
| retval = process_reply(pid, CMD_SALT, "Invalid number of rounds"); |
| } else { |
| encode_salt(ret, (u_int8_t*)csalt, csaltlen, log_rounds); |
| retval = process_reply(pid, CMD_SALT, ret); |
| } |
| erl_free_term(cslt); |
| erl_free_term(lr); |
| }; |
| erl_free_term(pattern); |
| return retval; |
| } |
| |
| static int |
| process_hashpw(ETERM *pid, ETERM *data) |
| { |
| int retval = 0; |
| ETERM *pattern, *pwd, *slt, *pwd_bin, *slt_bin; |
| char password[1024]; |
| char salt[1024]; |
| char encrypted[1024] = { 0 }; |
| |
| (void)memset(&password, '\0', sizeof(password)); |
| (void)memset(&salt, '\0', sizeof(salt)); |
| |
| pattern = erl_format("{Pass, Salt}"); |
| if (erl_match(pattern, data)) { |
| pwd = erl_var_content(pattern, "Pass"); |
| pwd_bin = erl_iolist_to_binary(pwd); |
| slt = erl_var_content(pattern, "Salt"); |
| slt_bin = erl_iolist_to_binary(slt); |
| if (ERL_BIN_SIZE(pwd_bin) > sizeof(password)) { |
| retval = process_reply(pid, CMD_HASHPW, "Password too long"); |
| } else if (ERL_BIN_SIZE(slt_bin) > sizeof(salt)) { |
| retval = process_reply(pid, CMD_HASHPW, "Salt too long"); |
| } else { |
| memcpy(password, ERL_BIN_PTR(pwd_bin), ERL_BIN_SIZE(pwd_bin)); |
| memcpy(salt, ERL_BIN_PTR(slt_bin), ERL_BIN_SIZE(slt_bin)); |
| if (bcrypt(encrypted, password, salt)) { |
| retval = process_reply(pid, CMD_HASHPW, "Invalid salt"); |
| } else { |
| retval = process_reply(pid, CMD_HASHPW, encrypted); |
| } |
| } |
| erl_free_term(pwd); |
| erl_free_term(slt); |
| erl_free_term(pwd_bin); |
| erl_free_term(slt_bin); |
| }; |
| erl_free_term(pattern); |
| return retval; |
| } |
| |
| static int |
| process_command(unsigned char *buf) |
| { |
| int retval = 0; |
| ETERM *pattern, *tuple, *cmd, *port, *data; |
| pattern = erl_format("{Cmd, Port, Data}"); |
| tuple = erl_decode(buf); |
| if (erl_match(pattern, tuple)) { |
| cmd = erl_var_content(pattern, "Cmd"); |
| port = erl_var_content(pattern, "Port"); |
| data = erl_var_content(pattern, "Data"); |
| switch (ERL_INT_VALUE(cmd)) { |
| case CMD_SALT: |
| retval = process_encode_salt(port, data); |
| break; |
| case CMD_HASHPW: |
| retval = process_hashpw(port, data); |
| break; |
| }; |
| erl_free_term(cmd); |
| erl_free_term(port); |
| erl_free_term(data); |
| } |
| erl_free_term(pattern); |
| erl_free_term(tuple); |
| return retval; |
| } |
| |
| static void |
| loop(void) |
| { |
| byte buf[BUFSIZE]; |
| int retval = 0; |
| do { |
| if (read_cmd(buf) > 0) |
| retval = process_command(buf); |
| else |
| retval = 0; |
| } while (retval); |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| erl_init(NULL, 0); |
| loop(); |
| return 0; |
| } |