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);
88 ctx->reply = json_parse(ctx->json, &rp_fb);
96 if (ctx->reply->type != JSON_OBJECT)
98 pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: Top-level node is not an object");
111 static bool check_auth(struct context *ctx)
115 ctx->json = json_new();
116 struct json_node *rq = json_new_object(ctx->json);
117 json_object_set(rq, "cmd", json_new_string(ctx->json, "login"));
118 json_object_set(rq, "zone", json_new_string(ctx->json, ctx->zone));
119 json_object_set(rq, "login", json_new_string(ctx->json, ctx->user));
120 json_object_set(rq, "passwd", json_new_string(ctx->json, ctx->passwd));
122 struct json_node *rp = run_command(ctx, rq);
125 struct json_node *error = json_object_get(rp, "error");
126 if (!error || error->type != JSON_STRING)
128 pam_syslog(ctx->pamh, LOG_ERR, "Malformed reply: No error status found");
131 if (!strcmp(error->string, ""))
134 pam_syslog(ctx->pamh, LOG_DEBUG, "Server returned error: %s", error->string);
137 json_delete(ctx->json);
141 int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv)
143 struct context ctx = {
147 .socket_path = INSTALL_RUN_DIR "/subauthd.socket",
149 .pool = mp_new(4096),
153 for (int i=0; i<argc; i++)
155 const char *arg = argv[i];
156 if (!strcmp(arg, "debug"))
158 else if (!strcmp(arg, "use_first_pass"))
159 ctx.use_first_pass = 1;
160 else if (str_has_prefix(arg, "socket="))
161 ctx.socket_path = arg + 7;
162 else if (str_has_prefix(arg, "zone="))
165 pam_syslog(pamh, LOG_ERR, "Unrecognized argument %s", arg);
169 if (pam_get_user(pamh, &ctx.user, NULL) != PAM_SUCCESS)
171 pam_syslog(pamh, LOG_ERR, "Cannot retrieve login");
176 if (ctx.use_first_pass)
178 if (pam_get_item(pamh, PAM_AUTHTOK, (const void **) &ctx.passwd) != PAM_SUCCESS)
184 if (pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &passwd, "Password: ") != PAM_SUCCESS)
186 ctx.passwd = mp_strdup(ctx.pool, passwd);
191 pam_syslog(pamh, LOG_DEBUG, "Authenticating user=%s zone=%s via socket %s", ctx.user, ctx.zone, ctx.socket_path);
193 if (!check_auth(&ctx))
203 int pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)