/* * ejabberd, Copyright (C) 2002-2007 Process-one * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA * */ #include #include #include #include #include #include #include #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_AUTH 0 #define CMD_ACCT 1 typedef unsigned char byte; #ifdef PAM_FAIL_DELAY static void delay_fn(int retval, unsigned usec_delay, void *appdata_ptr) { /* No delay. However, looks like some PAM modules ignore this */ } #endif static int misc_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *password) { int msg_style; if (num_msg != 1) return PAM_CONV_ERR; msg_style = msg[0]->msg_style; if ((msg_style != PAM_PROMPT_ECHO_OFF) && (msg_style != PAM_PROMPT_ECHO_ON)) return PAM_CONV_ERR; *resp = malloc(sizeof(struct pam_response)); (*resp)[0].resp_retcode = 0; (*resp)[0].resp = strdup(password); return PAM_SUCCESS; } static int auth(char *service, char *user, char *password) { struct pam_conv conv = {misc_conv, password}; int retval; pam_handle_t *pamh = NULL; retval = pam_start(service, user, &conv, &pamh); if (retval == PAM_SUCCESS) retval = pam_set_item(pamh, PAM_RUSER, user); #ifdef PAM_FAIL_DELAY if (retval == PAM_SUCCESS) retval = pam_set_item(pamh, PAM_FAIL_DELAY, (void *)delay_fn); #endif if (retval == PAM_SUCCESS) retval = pam_authenticate(pamh, 0); if (retval == PAM_SUCCESS) retval = pam_acct_mgmt(pamh, 0); pam_end(pamh, retval); return retval; } static int acct_mgmt(char *service, char *user) { struct pam_conv conv = {misc_conv, NULL}; int retval; pam_handle_t *pamh = NULL; retval = pam_start(service, user, &conv, &pamh); if (retval == PAM_SUCCESS) retval = pam_set_item(pamh, PAM_RUSER, user); #ifdef PAM_FAIL_DELAY if (retval == PAM_SUCCESS) retval = pam_set_item(pamh, PAM_FAIL_DELAY, (void *)delay_fn); #endif if (retval == PAM_SUCCESS) retval = pam_acct_mgmt(pamh, 0); pam_end(pamh, retval); return retval; } 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, char *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(char *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, int res) { ETERM *result; int len, retval; const char *errtxt; byte *buf; if (res == PAM_SUCCESS) result = erl_format("{~i, ~w, true}", cmd, pid); else { errtxt = pam_strerror(NULL, res); result = erl_format("{~i, ~w, {false, ~s}}", cmd, pid, errtxt); } 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_acct(ETERM *pid, ETERM *data) { int retval = 0; ETERM *pattern, *srv, *user; char *service, *username; pattern = erl_format("{Srv, User}"); if (erl_match(pattern, data)) { srv = erl_var_content(pattern, "Srv"); service = erl_iolist_to_string(srv); user = erl_var_content(pattern, "User"); username = erl_iolist_to_string(user); retval = process_reply(pid, CMD_ACCT, acct_mgmt(service, username)); erl_free_term(srv); erl_free_term(user); erl_free(service); erl_free(username); } erl_free_term(pattern); return retval; } static int process_auth(ETERM *pid, ETERM *data) { int retval = 0; ETERM *pattern, *srv, *user, *pass; char *service, *username, *password; pattern = erl_format("{Srv, User, Pass}"); if (erl_match(pattern, data)) { srv = erl_var_content(pattern, "Srv"); service = erl_iolist_to_string(srv); user = erl_var_content(pattern, "User"); username = erl_iolist_to_string(user); pass = erl_var_content(pattern, "Pass"); password = erl_iolist_to_string(pass); retval = process_reply(pid, CMD_AUTH, auth(service, username, password)); erl_free_term(srv); erl_free_term(user); erl_free_term(pass); erl_free(service); erl_free(username); erl_free(password); }; erl_free_term(pattern); return retval; } static int process_command(byte *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_AUTH: retval = process_auth(port, data); break; case CMD_ACCT: retval = process_acct(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; }