aboutsummaryrefslogtreecommitdiff
path: root/src/pam/epam.c
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2007-10-01 10:49:42 +0000
committerAlexey Shchepin <alexey@process-one.net>2007-10-01 10:49:42 +0000
commit4971de9d2a18da45a6a36d877934eedbee6afdf2 (patch)
tree3fbda3dd4c0c3504ca2368c6927b8e903dbb39ae /src/pam/epam.c
parent* src/ejabberd_auth_pam.erl: Support for PAM authentication (diff)
SVN Revision: 954
Diffstat (limited to 'src/pam/epam.c')
-rw-r--r--src/pam/epam.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/pam/epam.c b/src/pam/epam.c
new file mode 100644
index 000000000..b365c0a68
--- /dev/null
+++ b/src/pam/epam.c
@@ -0,0 +1,250 @@
+#include <security/pam_appl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <erl_interface.h>
+#include <ei.h>
+#include <unistd.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_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;
+}