2 * Sub-authentication Client
4 * (c) 2017 Martin Mares <mj@ucw.cz>
10 #include <ucw/mempool.h>
11 #include <ucw/string.h>
12 #include <ucw/trans.h>
13 #include <ucw-json/json.h>
19 #include <sys/socket.h>
28 #include <security/pam_modules.h>
29 #include <security/pam_ext.h>
35 const char *socket_path;
40 struct json_context *json;
41 struct json_node *reply;
44 static struct json_node *run_command(struct context *ctx, struct json_node *request)
48 int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
51 pam_syslog(ctx->pamh, LOG_ERR, "socket(PF_UNIX, SOCK_SEQPACKET): %m");
55 struct sockaddr_un sun;
56 sun.sun_family = AF_UNIX;
57 if (strlen(ctx->socket_path) >= sizeof(sun.sun_path))
59 pam_syslog(ctx->pamh, LOG_ERR, "Socket path too long");
62 strcpy(sun.sun_path, ctx->socket_path);
64 if (connect(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
66 pam_syslog(ctx->pamh, LOG_ERR, "Cannot connect to %s: %m", ctx->socket_path);
70 struct fastbuf *rq_fb = fbgrow_create(4096);
71 json_write(ctx->json, rq_fb, request);
73 uint rq_len = fbgrow_get_buf(rq_fb, &rq_buf);
74 if (send(sk, rq_buf, rq_len, 0) < 0)
76 pam_syslog(ctx->pamh, LOG_ERR, "Cannot send request: %m");
80 uint rp_bufsize = 16384;
81 byte *rp_buf = xmalloc(rp_bufsize);
82 int rp_len = recv(sk, rp_buf, rp_bufsize, 0);
85 pam_syslog(ctx->pamh, LOG_ERR, "Cannot receive reply: %m");
90 fbbuf_init_read(&rp_fb, rp_buf, rp_len, 0);
94 ctx->reply = json_parse(ctx->json, &rp_fb);
102 if (ctx->reply->type != JSON_OBJECT)
104 pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: Top-level node is not an object");
117 static bool check_auth(struct context *ctx)
121 ctx->json = json_new();
122 struct json_node *rq = json_new_object(ctx->json);
123 json_object_set(rq, "cmd", json_new_string(ctx->json, "login"));
124 json_object_set(rq, "zone", json_new_string(ctx->json, ctx->zone));
125 json_object_set(rq, "login", json_new_string(ctx->json, ctx->user));
126 json_object_set(rq, "passwd", json_new_string(ctx->json, ctx->passwd));
128 struct json_node *rp = run_command(ctx, rq);
131 struct json_node *error = json_object_get(rp, "error");
132 if (!error || error->type != JSON_STRING)
134 pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: No error status found");
137 if (!strcmp(error->string, ""))
140 pam_syslog(ctx->pamh, LOG_DEBUG, "Server returned error: %s", error->string);
143 json_delete(ctx->json);
147 int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv)
149 struct context ctx = {
153 .socket_path = INSTALL_RUN_DIR "/subauthd.socket",
155 .pool = mp_new(4096),
159 for (int i=0; i<argc; i++)
161 const char *arg = argv[i];
162 if (!strcmp(arg, "debug"))
164 else if (!strcmp(arg, "use_first_pass"))
165 ctx.use_first_pass = 1;
166 else if (str_has_prefix(arg, "socket="))
167 ctx.socket_path = arg + 7;
168 else if (str_has_prefix(arg, "zone="))
171 pam_syslog(pamh, LOG_ERR, "Unrecognized argument %s", arg);
175 if (pam_get_user(pamh, &ctx.user, NULL) != PAM_SUCCESS)
177 pam_syslog(pamh, LOG_ERR, "Cannot retrieve login");
182 if (ctx.use_first_pass)
184 if (pam_get_item(pamh, PAM_AUTHTOK, (const void **) &ctx.passwd) != PAM_SUCCESS)
190 if (pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &passwd, "Password: ") != PAM_SUCCESS)
192 ctx.passwd = mp_strdup(ctx.pool, passwd);
197 pam_syslog(pamh, LOG_DEBUG, "Authenticating user=%s zone=%s via socket %s", ctx.user, ctx.zone, ctx.socket_path);
199 if (!check_auth(&ctx))
209 int pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)