]> mj.ucw.cz Git - subauth.git/blob - client/subauth.c
30dd28cdd1a047b1ccd8f5317ab35cd2a4ea8eae
[subauth.git] / client / subauth.c
1 /*
2  *      Sub-authentication Client
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/opt.h>
9 #include <ucw-json/json.h>
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15 #include <unistd.h>
16
17 #include "autoconf.h"
18
19 /*** Arguments ***/
20
21 static char *socket_path = INSTALL_RUN_DIR "/subauthd.socket";
22
23 static char *arg_login;
24 static char *arg_zone;
25 static char *arg_ident;
26 static int debug;
27
28 /*** Communication with the daemon ***/
29
30 static struct json_context *js;
31 static struct json_node *rq;
32 static struct json_node *rp;
33
34 static void set_string(struct json_node *n, const char *key, const char *val)
35 {
36   json_object_set(n, key, json_new_string(js, val));
37 }
38
39 static struct json_node *get_child(struct json_node *obj, const char *key, uint type)
40 {
41   struct json_node *n = json_object_get(obj, key);
42   if (n && n->type != type)
43     die("Malformed reply: %s has wrong type", key);
44   return n;
45 }
46
47 static struct json_node *need_child(struct json_node *obj, const char *key, uint type)
48 {
49   struct json_node *n = get_child(obj, key, type);
50   if (!n)
51     die("Malformed reply: missing %s", key);
52   return n;
53 }
54
55 static void op_new(const char *op)
56 {
57   js = json_new();
58   rq = json_new_object(js);
59   set_string(rq, "cmd", op);
60 }
61
62 static void op_debug(const char *msg, struct json_node *n)
63 {
64   if (!debug)
65     return;
66
67   struct fastbuf *out = bfdopen_shared(1, 4096);
68   bprintf(out, ">>> %s\n", msg);
69   js->format_options = JSON_FORMAT_INDENT;
70   json_write(js, out, n);
71   js->format_options = 0;
72   bclose(out);
73 }
74
75 static void op_run(void)
76 {
77   int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
78   if (sk < 0)
79     die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
80
81   struct sockaddr_un sun;
82   sun.sun_family = AF_UNIX;
83   if (strlen(socket_path) >= sizeof(sun.sun_path))
84     die("Socket path too long");
85   strcpy(sun.sun_path, socket_path);
86
87   if (connect(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
88     die("Cannot connect to %s: %m", socket_path);
89
90   op_debug("Request", rq);
91
92   struct fastbuf *rq_fb = fbgrow_create(4096);
93   json_write(js, rq_fb, rq);
94   byte *rq_buf;
95   uint rq_len = fbgrow_get_buf(rq_fb, &rq_buf);
96   if (send(sk, rq_buf, rq_len, 0) < 0)
97     die("Cannot send request: %m");
98   bclose(rq_fb);
99
100   byte rp_buf[4096];            // FIXME
101   int rp_len = recv(sk, rp_buf, sizeof(rp_buf), 0);
102   if (rp_len < 0)
103     die("Cannot receive reply: %m");
104
105   struct fastbuf rp_fb;
106   fbbuf_init_read(&rp_fb, rp_buf, rp_len, 0);
107   rp = json_parse(js, &rp_fb);
108   op_debug("Reply", rp);
109
110   close(sk);
111
112   if (rp->type != JSON_OBJECT)
113     die("Malformed reply: Top-level node is not an object");
114   struct json_node *err = need_child(rp, "error", JSON_STRING);
115   if (strcmp(err->string, ""))
116     {
117       fprintf(stderr, "Error: %s\n", err->string);
118       exit(1);
119     }
120 }
121
122 /*** Commands ***/
123
124 static void cmd_set_passwd(void)
125 {
126 }
127
128 static void cmd_list(void)
129 {
130   op_new("list-accts");
131   op_run();
132 }
133
134 static void cmd_zones(void)
135 {
136   op_new("list-zones");
137   op_run();
138
139   struct json_node **jzs = need_child(rp, "zones", JSON_ARRAY)->elements;
140   printf("%-16s %6s %6s  %s\n", "Zone", "Passwd", "Tokens", "Description");
141   for (uint i=0; i < GARY_SIZE(jzs); i++)
142     {
143       struct json_node *jz = jzs[i];
144       struct json_node *jname = need_child(jz, "name", JSON_STRING);
145       struct json_node *jdesc = get_child(jz, "desc", JSON_STRING);
146       struct json_node *jpass = get_child(jz, "allow-passwd", JSON_NUMBER);
147       struct json_node *jtokens = get_child(jz, "allow-tokens", JSON_NUMBER);
148       printf("%-16s %6s %6d  %s\n",
149         jname->string,
150         (jpass && jpass->number ? "yes" : "-"),
151         (jtokens ? (uint) jtokens->number : 0),
152         (jdesc ? jdesc->string : "(no description)"));
153     }
154 }
155
156 static void cmd_raw(void)
157 {
158   op_new("");
159
160   struct fastbuf *in = bfdopen_shared(0, 4096);
161   rq = json_parse(js, in);
162   bclose(in);
163
164   op_run();
165 }
166
167 enum command {
168   CMD_SET_PASSWD,
169   CMD_LIST,
170   CMD_ZONES,
171   CMD_RAW,
172   CMD_MAX
173 };
174
175 void (* const command_handlers[CMD_MAX])(void) = {
176   [CMD_SET_PASSWD] = cmd_set_passwd,
177   [CMD_LIST] = cmd_list,
178   [CMD_ZONES] = cmd_zones,
179   [CMD_RAW] = cmd_raw,
180 };
181
182 static int command = -1;
183
184 /*** Main ***/
185
186 static const struct opt_section options = {
187   OPT_ITEMS {
188     OPT_HELP("A client to the sub-authentication daemon."),
189     OPT_HELP("Usage: subauth [general options] <command> [options]"),
190     OPT_HELP(""),
191     OPT_HELP("General options:"),
192     OPT_STRING('s', "socket", socket_path, OPT_REQUIRED_VALUE, "path\tPath to daemon control socket"),
193     OPT_BOOL(0, "debug", debug, 0, "\tDump raw communication with the daemon"),
194     OPT_HELP_OPTION,
195     OPT_HELP(""),
196     OPT_HELP("Commands:"),
197     OPT_SWITCH(0, "passwd", command, CMD_SET_PASSWD, OPT_SINGLE, "\tSet password"),
198     OPT_SWITCH(0, "list", command, CMD_LIST, OPT_SINGLE, "\tList tokens for all zones"),
199     OPT_SWITCH(0, "zones", command, CMD_ZONES, OPT_SINGLE, "\tList all known zones"),
200     OPT_SWITCH(0, "raw", command, CMD_RAW, OPT_SINGLE, "\tSend raw JSON command to the daemon"),
201     OPT_HELP(""),
202     OPT_HELP("Command options:"),
203     OPT_STRING('u', "user", arg_login, OPT_REQUIRED_VALUE, "login\tUser to act on (default: whoever calls it)"),
204     OPT_STRING('z', "zone", arg_zone, OPT_REQUIRED_VALUE, "zone\tAuthentication zone"),
205     OPT_STRING('i', "ident", arg_ident, OPT_REQUIRED_VALUE, "id\tToken ID"),
206     OPT_END
207   }
208 };
209
210 int main(int argc UNUSED, char **argv)
211 {
212   opt_parse(&options, argv+1);
213
214   if (command < 0)
215     opt_failure("No command given");
216   if (!command_handlers[command])
217     opt_failure("Command not implemented");
218   command_handlers[command]();
219
220   return 0;
221 }