2 * Sub-authentication Client
4 * (c) 2017 Martin Mares <mj@ucw.cz>
8 #include <ucw/mempool.h>
9 #include <ucw/string.h>
10 #include <ucw/trans.h>
11 #include <ucw-json/json.h>
14 #include <sys/socket.h>
22 #include <security/pam_modules.h>
23 #include <security/pam_ext.h>
29 const char *socket_path;
34 struct json_context *json;
35 struct json_node *reply;
38 static struct json_node *run_command(struct context *ctx, struct json_node *request)
42 int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
45 pam_syslog(ctx->pamh, LOG_ERR, "socket(PF_UNIX, SOCK_SEQPACKET): %m");
49 struct sockaddr_un sun;
50 sun.sun_family = AF_UNIX;
51 if (strlen(ctx->socket_path) >= sizeof(sun.sun_path))
53 pam_syslog(ctx->pamh, LOG_ERR, "Socket path too long");
56 strcpy(sun.sun_path, ctx->socket_path);
58 if (connect(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
60 pam_syslog(ctx->pamh, LOG_ERR, "Cannot connect to %s: %m", ctx->socket_path);
64 struct fastbuf *rq_fb = fbgrow_create(4096);
65 json_write(ctx->json, rq_fb, request);
67 uint rq_len = fbgrow_get_buf(rq_fb, &rq_buf);
68 if (send(sk, rq_buf, rq_len, 0) < 0)
70 pam_syslog(ctx->pamh, LOG_ERR, "Cannot send request: %m");
74 uint rp_bufsize = 16384;
75 byte *rp_buf = xmalloc(rp_bufsize);
76 int rp_len = recv(sk, rp_buf, rp_bufsize, 0);
79 pam_syslog(ctx->pamh, LOG_ERR, "Cannot receive reply: %m");
84 fbbuf_init_read(&rp_fb, rp_buf, rp_len, 0);
86 struct json_node *reply = NULL;
89 reply = json_parse(ctx->json, &rp_fb);
97 if (reply->type != JSON_OBJECT)
99 pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: Top-level node is not an object");
114 static bool check_auth(struct context *ctx)
118 ctx->json = json_new();
119 struct json_node *rq = json_new_object(ctx->json);
120 json_object_set(rq, "cmd", json_new_string(ctx->json, "login"));
121 json_object_set(rq, "zone", json_new_string(ctx->json, ctx->zone));
122 json_object_set(rq, "login", json_new_string(ctx->json, ctx->user));
123 json_object_set(rq, "passwd", json_new_string(ctx->json, ctx->passwd));
125 struct json_node *rp = run_command(ctx, rq);
128 struct json_node *error = json_object_get(rp, "error");
129 if (!error || error->type != JSON_STRING)
131 pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: No error status found");
134 if (!strcmp(error->string, ""))
137 pam_syslog(ctx->pamh, LOG_DEBUG, "Server returned error: %s", error->string);
140 json_delete(ctx->json);
144 int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv)
146 struct context ctx = {
150 .socket_path = INSTALL_RUN_DIR "/subauthd.socket",
152 .pool = mp_new(4096),
156 for (int i=0; i<argc; i++)
158 const char *arg = argv[i];
159 if (!strcmp(arg, "debug"))
161 else if (!strcmp(arg, "use_first_pass"))
162 ctx.use_first_pass = 1;
163 else if (str_has_prefix(arg, "socket="))
164 ctx.socket_path = arg + 7;
165 else if (str_has_prefix(arg, "zone="))
168 pam_syslog(pamh, LOG_ERR, "Unrecognized argument %s", arg);
172 if (pam_get_user(pamh, &ctx.user, NULL) != PAM_SUCCESS)
174 pam_syslog(pamh, LOG_ERR, "Cannot retrieve login");
179 if (ctx.use_first_pass)
181 if (pam_get_item(pamh, PAM_AUTHTOK, (const void **) &ctx.passwd) != PAM_SUCCESS)
187 if (pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &passwd, "Password: ") != PAM_SUCCESS)
189 ctx.passwd = mp_strdup(ctx.pool, passwd);
194 pam_syslog(pamh, LOG_DEBUG, "Authenticating user=%s zone=%s via socket %s", ctx.user, ctx.zone, ctx.socket_path);
196 if (!check_auth(&ctx))
206 int pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)