]> mj.ucw.cz Git - subauth.git/commitdiff
PAM module
authorMartin Mares <mj@ucw.cz>
Sun, 23 Jul 2017 16:09:54 +0000 (18:09 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 23 Jul 2017 16:09:54 +0000 (18:09 +0200)
Makefile
pam/Makefile [new file with mode: 0644]
pam/pam_subauth.c [new file with mode: 0644]

index d538520cd4dbcfdbb64c7e97e94d79a4414d4eb1..830d7ecf5fcdf3449401ddc808be5b829da56314 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,7 @@ LIBS+=$(LIBUCW_LIBS) $(LIBUCW_JSON_LIBS)
 
 include $(s)/server/Makefile
 include $(s)/client/Makefile
+include $(s)/pam/Makefile
 
 # And finally the default rules of the build system
 include $(BUILDSYS)/Makebottom
diff --git a/pam/Makefile b/pam/Makefile
new file mode 100644 (file)
index 0000000..06e68ad
--- /dev/null
@@ -0,0 +1,6 @@
+DIRS+=pam
+
+PROGS+=$(o)/pam/pam_subauth.so
+
+$(o)/pam/pam_subauth.so: $(o)/pam/pam_subauth.oo
+$(o)/pam/pam_subauth.so: LIBS+=-lpam
diff --git a/pam/pam_subauth.c b/pam/pam_subauth.c
new file mode 100644 (file)
index 0000000..dd9340e
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *     Sub-authentication Client
+ *
+ *     (c) 2017 Martin Mares <mj@ucw.cz>
+ */
+
+// FIXME: Includes
+
+#include <ucw/lib.h>
+#include <ucw/mempool.h>
+#include <ucw/string.h>
+#include <ucw/trans.h>
+#include <ucw-json/json.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "autoconf.h"
+
+#define PAM_SM_AUTH
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+
+struct context {
+  pam_handle_t *pamh;
+  int debug;
+  int use_first_pass;
+  const char *socket_path;
+  const char *zone;
+  const char *user;
+  const char *passwd;
+  struct mempool *pool;
+  struct json_context *json;
+  struct json_node *reply;
+};
+
+static struct json_node *run_command(struct context *ctx, struct json_node *request)
+{
+  ctx->reply = NULL;
+
+  int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
+  if (sk < 0)
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "socket(PF_UNIX, SOCK_SEQPACKET): %m");
+      return NULL;
+    }
+
+  struct sockaddr_un sun;
+  sun.sun_family = AF_UNIX;
+  if (strlen(ctx->socket_path) >= sizeof(sun.sun_path))
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "Socket path too long");
+      goto fail1;
+    }
+  strcpy(sun.sun_path, ctx->socket_path);
+
+  if (connect(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "Cannot connect to %s: %m", ctx->socket_path);
+      goto fail1;
+    }
+
+  struct fastbuf *rq_fb = fbgrow_create(4096);
+  json_write(ctx->json, rq_fb, request);
+  byte *rq_buf;
+  uint rq_len = fbgrow_get_buf(rq_fb, &rq_buf);
+  if (send(sk, rq_buf, rq_len, 0) < 0)
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "Cannot send request: %m");
+      goto fail2;
+    }
+
+  uint rp_bufsize = 16384;
+  byte *rp_buf = xmalloc(rp_bufsize);
+  int rp_len = recv(sk, rp_buf, rp_bufsize, 0);
+  if (rp_len < 0)
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "Cannot receive reply: %m");
+      goto fail3;
+    }
+
+  struct fastbuf rp_fb;
+  fbbuf_init_read(&rp_fb, rp_buf, rp_len, 0);
+
+  TRANS_TRY
+    {
+      ctx->reply = json_parse(ctx->json, &rp_fb);
+    }
+  TRANS_CATCH(x)
+    {
+      goto fail3;
+    }
+  TRANS_END;
+
+  if (ctx->reply->type != JSON_OBJECT)
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: Top-level node is not an object");
+      goto fail3;
+    }
+
+fail3:
+  free(rp_buf);
+fail2:
+  bclose(rq_fb);
+fail1:
+  close(sk);
+  return ctx->reply;
+}
+
+static bool check_auth(struct context *ctx)
+{
+  bool ok = 0;
+
+  ctx->json = json_new();
+  struct json_node *rq = json_new_object(ctx->json);
+  json_object_set(rq, "cmd", json_new_string(ctx->json, "login"));
+  json_object_set(rq, "zone", json_new_string(ctx->json, ctx->zone));
+  json_object_set(rq, "login", json_new_string(ctx->json, ctx->user));
+  json_object_set(rq, "passwd", json_new_string(ctx->json, ctx->passwd));
+
+  struct json_node *rp = run_command(ctx, rq);
+  if (!rp)
+    goto done;
+  struct json_node *error = json_object_get(rp, "error");
+  if (!error || error->type != JSON_STRING)
+    {
+      pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: No error status found");
+      goto done;
+    }
+  if (!strcmp(error->string, ""))
+    ok = 1;
+  else if (ctx->debug)
+    pam_syslog(ctx->pamh, LOG_DEBUG, "Server returned error: %s", error->string);
+
+done:
+  json_delete(ctx->json);
+  return ok;
+}
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv)
+{
+  struct context ctx = {
+    .pamh = pamh,
+    .debug = 0,
+    .use_first_pass = 0,
+    .socket_path = INSTALL_RUN_DIR "/subauthd.socket",
+    .zone = "default",
+    .pool = mp_new(4096),
+  };
+
+  // Parse arguments
+  for (int i=0; i<argc; i++)
+    {
+      const char *arg = argv[i];
+      if (!strcmp(arg, "debug"))
+       ctx.debug = 1;
+      else if (!strcmp(arg, "use_first_pass"))
+       ctx.use_first_pass = 1;
+      else if (str_has_prefix(arg, "socket="))
+       ctx.socket_path = arg + 7;
+      else if (str_has_prefix(arg, "zone="))
+       ctx.zone = arg + 5;
+      else
+       pam_syslog(pamh, LOG_ERR, "Unrecognized argument %s", arg);
+    }
+
+  // Obtain user name
+  if (pam_get_user(pamh, &ctx.user, NULL) != PAM_SUCCESS)
+    {
+      pam_syslog(pamh, LOG_ERR, "Cannot retrieve login");
+      goto fail;
+    }
+
+  // Obtain password
+  if (ctx.use_first_pass)
+    {
+      if (pam_get_item(pamh, PAM_AUTHTOK, (const void **) &ctx.passwd) != PAM_SUCCESS)
+       goto fail;
+    }
+  else
+    {
+      char *passwd;
+      if (pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &passwd, "Password: ") != PAM_SUCCESS)
+       return PAM_AUTH_ERR;
+      ctx.passwd = mp_strdup(ctx.pool, passwd);
+      free(passwd);
+    }
+
+  if (ctx.debug)
+    pam_syslog(pamh, LOG_DEBUG, "Authenticating user=%s zone=%s via socket %s", ctx.user, ctx.zone, ctx.socket_path);
+
+  if (!check_auth(&ctx))
+    goto fail;
+
+  return PAM_SUCCESS;
+
+fail:
+  mp_delete(ctx.pool);
+  return PAM_AUTH_ERR;
+}
+
+int pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)
+{
+  return PAM_SUCCESS;
+}