1 /* zxbusent.c - Audit Bus Entity management
2 * Copyright (c) 2012 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 * This is confidential unpublished proprietary source code of the author.
4 * NO WARRANTY, not even implied warranties. Contains trade secrets.
5 * Distribution prohibited unless authorized in writing. See file COPYING.
6 * Special grant: http.c may be used with zxid open source project under
7 * same licensing terms as zxid itself.
8 * $Id$
9 *
10 * 16.8.2012, created --Sampo
11 * 30.8.2012, added subscription mechanisms --Sampo
12 * 5.9.2012, separated entity management and subscriptions to own source files --Sampo
13 */
14
15 #include "platform.h"
16 #include "errmac.h"
17 #include "akbox.h"
18 #include "hiios.h"
19 #include "hiproto.h"
20 #include <zx/zxidconf.h>
21 #include <zx/zxidutil.h>
22
23 #define __USE_GNU 1 /* for O_DIRECT */
24
25 #include <ctype.h>
26 #include <memory.h>
27 #include <stdlib.h>
28 #include <netinet/in.h> /* htons(3) and friends */
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <errno.h>
33
34 /* Alias some struct fields for headers that can not be seen together. */
35 #define receipt host
36 #define rcpt_id host
37 #define acpt_vers vers
38 #define tx_id vers
39 #define session login
40 #define subs_id login
41 #define subsc login
42 #define server pw
43 #define ack pw
44 #define msg_id pw
45 #define heart_bt dest
46 #define zx_rcpt_sig dest
47
48 extern int verbose; /* defined in option parsing in zxbusd.c */
49 extern char* zxbus_path;
50
51 /*() Allocate a bus entity
52 * locking:: must be called inside shf->ent_mut
53 * return:: pointer to hi_ent on success, 0 on failure */
54
55 /* Called by: zxbus_load_ent, zxbus_login_ent, zxbus_login_subj_hash */
zxbus_new_ent(struct hiios * shf,int len,const char * eid)56 struct hi_ent* zxbus_new_ent(struct hiios* shf, int len, const char* eid)
57 {
58 struct hi_ent* ent;
59 if (len == -1)
60 len = strlen(eid);
61 ZMALLOC(ent);
62 ent->n = shf->ents;
63 shf->ents = ent;
64 ZMALLOCN(ent->chs, shf->max_chs);
65 MALLOCN(ent->eid, len+1);
66 memcpy(ent->eid, eid, len);
67 ent->eid[len] = 0;
68 return ent;
69 }
70
71 /*() Allocate a bus entity (and check that it exists)
72 * The bus entities are special users in /var/zxid/bus/uid/
73 * hierarchy. The user name is the succinct id formed
74 * by sha1-base64 of the Entity ID. New bus entities can be provisioned
75 * using the zxpasswd(1) tool, e.g.
76 *
77 * echo -n 'pw123' | ./zxpasswd -new 2E_uLovDu748vn9dWEM6tqVzqUQ /var/zxid/bus/uid/
78 *
79 * locking:: must be called inside shf->ent_mut
80 * return:: pointer to hi_ent on success, 0 on failure */
81
82 /* Called by: zxbus_load_acks, zxbus_load_ch_subs, zxbus_login_ent, zxbus_login_subj_hash */
zxbus_load_ent(struct hiios * shf,int len,const char * eid)83 struct hi_ent* zxbus_load_ent(struct hiios* shf, int len, const char* eid)
84 {
85 char eid_buf[256];
86 char sha1_name[28];
87 char u_path[ZXID_MAX_BUF];
88 struct hi_ent* ent;
89 struct stat st;
90
91 if (len == -1)
92 len = strlen(eid);
93
94 /* Check if already loaded */
95
96 for (ent = shf->ents; ent; ent = ent->n) {
97 DD("Checking eid(%.*s) against ent_%p->eid(%s)", len, eid, ent, ent->eid);
98 if (!memcmp(ent->eid, eid, len) && !ent->eid[len]) {
99 DD("Found ent_%p->eid(%s) io(%x) ache_%p", ent, ent->eid, ent->io?ent->io->fd:0xdeadbeef, ent->acks);
100 return ent;
101 }
102 }
103
104 /* Seems not. Prepare path and check if user directory exists. */
105
106 if (len > sizeof(eid_buf)-2) {
107 ERR("Entity ID too long (%.*s) len=%d", len, eid, len);
108 return 0;
109 }
110 memcpy(eid_buf, eid, len);
111 eid_buf[len] = 0;
112
113 sha1_safe_base64(sha1_name, len, eid_buf);
114 sha1_name[27] = 0;
115
116 name_from_path(u_path, sizeof(u_path), "%s" ZXID_UID_DIR "/%s/", zxbus_path, sha1_name);
117 if (stat(u_path, &st)==-1) {
118 D("Entity(%.*s) does not exit. path(%s)", len, eid, u_path);
119 return 0;
120 }
121
122 /* Add newly allocated entity to the list. */
123
124 ent = zxbus_new_ent(shf, len, eid);
125 D("Loaded ent_%p->eid(%s) io(%x) ache_%p",ent,ent->eid,ent->io?ent->io->fd:0xdeadbeef,ent->acks);
126 return ent;
127 }
128
129 /*() Perform zxbus specifics to call generic zx_password_authn() */
130
131 /* Called by: zxbus_login_ent */
zxbus_pw_authn_ent(const char * eid,const char * passw,int fd_hint)132 static int zxbus_pw_authn_ent(const char* eid, const char* passw, int fd_hint)
133 {
134 char sha1_name[28];
135 char eid_buf[256];
136 char pw_buf[256];
137 int len = strlen(eid);
138
139 if (len > sizeof(eid_buf)-2) {
140 ERR("Entity ID too long (%s) len=%d", eid, len);
141 return 0;
142 }
143 memcpy(eid_buf, eid, len);
144 eid_buf[len] = 0;
145
146 sha1_safe_base64(sha1_name, len, eid_buf);
147 sha1_name[27] = 0;
148
149 len = strchr(passw, '\n') - passw;
150 if (len > sizeof(pw_buf)-2) {
151 ERR("Password too long (%.*s) len=%d", len, passw, len);
152 return 0;
153 }
154 memcpy(pw_buf, passw, len);
155 pw_buf[len] = 0;
156
157 return zx_password_authn(zxbus_path, sha1_name, pw_buf, 0, fd_hint);
158 /* *** add password overwrite in memory */
159 }
160
161 /*() Login an entity, typically producer or listener.
162 * Here we may check credentials from TLS layer against login header, (*** TBD)
163 * or we may perform simple username password login using the headers.
164 * In any event the entity is either found in shf->ents list or
165 * it is added there. The entity is associated with io object (and vice versa).
166 * return:: 1 on success, 0 on failure.
167 *
168 * To create bus users, which use SHA1 of their EntityID (the entityID is passed
169 * in STOMP 1.1 login header) as username, you should follow these steps
170 *
171 * 1. Run ./zxbuslist -c 'URL=https://sp.foo.com/' -dc to determine the entity ID
172 * 2. Convert entity ID to SHA1 hash: ./zxcot -p 'http://sp.foo.com?o=B'
173 * 3. Create the user: ./zxpasswd -at 'eid: http://sp.foo.com?o=B' -new G2JpTSX_dbdJ7frhYNpKWGiMdTs /var/zxid/bus/uid/ <passwd
174 * 4. To enable ClientTLS authentication, determine the subject_hash of
175 * the encryption certificate and symlink that to the main account:
176 * > openssl x509 -subject_hash -noout </var/zxid/buscli/pem/enc-nopw-cert.pem
177 * 162553b8
178 * > ln -s /var/zxid/bus/uid/G2JpTSX_dbdJ7frhYNpKWGiMdTs /var/zxid/bus/uid/162553b8
179 */
180
181 /* Called by: stomp_got_login */
zxbus_login_ent(struct hi_thr * hit,struct hi_io * io,struct hi_pdu * req)182 int zxbus_login_ent(struct hi_thr* hit, struct hi_io* io, struct hi_pdu* req)
183 {
184 char* p;
185 char* login = req->ad.stomp.login;
186 struct hi_ent* ent;
187 int eidlen;
188 eidlen = strchr(login, '\n') - login;
189 login[eidlen] = 0; /* nul term */
190 DD("login_ent(%s) eidlen=%d", login, eidlen);
191 for (p = login; *p; ++p) /* Undo STOMP 1.1 forbidden ':' escaping */
192 if (*p == '|')
193 *p = ':';
194 D("login_ent(%s) eidlen=%d - deescaped", login, eidlen);
195 D("WILL LOCK ent_mut->thr=%lx (%s:%d)", (long)hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
196
197 LOCK(hit->shf->ent_mut, "login");
198 D("LOCK ent_mut->thr=%lx (%s:%d)", (long)hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
199 if (!(ent = zxbus_load_ent(hit->shf, eidlen, login))) {
200 if (hit->shf->anonlogin) {
201 ent = zxbus_new_ent(hit->shf, eidlen, login);
202 INFO("Anon login eid(%s)", ent->eid);
203 /* *** consider persisting the newly created account */
204 } else {
205 D("UNLOCK ent_mut->thr=%lx (%s:%d)", (long)hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
206 UNLOCK(hit->shf->ent_mut, "login-fail");
207 ERR("Login account(%s) does not exist and no anon login", login);
208 return 0;
209 }
210 }
211
212 if (req->ad.stomp.pw) {
213 if (!zxbus_pw_authn_ent(login, req->ad.stomp.pw, io->fd)) {
214 D("UNLOCK ent_mut->thr=%lx (%s:%d)", (long)hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
215 UNLOCK(hit->shf->ent_mut, "login-fail3");
216 return 0;
217 }
218 } else {
219 /* This could be ClientTLS */
220 if (!hi_vfy_peer_ssl_cred(hit, io, login)) {
221 D("UNLOCK ent_mut->thr=%lx (%s:%d)", (long)hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
222 UNLOCK(hit->shf->ent_mut, "login-fail5");
223 ERR("Login account(%s): no password supplied and no ClientTLS match", ent->eid);
224 return 0;
225 }
226 }
227
228 if (ent->io) {
229 if (ent->io == io) {
230 NEVER("Entity has io already set to current io_%p", io);
231 } else {
232 NEVER("Entity has io already set to different io_%p", ent->io);
233 }
234 }
235
236 ent->io = io;
237 LOCK(io->qel.mut, "login");
238 D("LOCK io(%p)->qel.mut->thr=%lx (%s:%d)", io, (long)io->qel.mut.thr, io->qel.mut.func, io->qel.mut.line);
239 if (io->ent) {
240 if (io->ent == ent) {
241 NEVER("io has ent already set to current ent_%p", ent);
242 } else {
243 NEVER("io has ent already set to different ent_%p", ent);
244 }
245 }
246 io->ent = ent;
247 D("Logged in ent_%p eid(%s) io_%p (%x)", ent, ent->eid, io, io->fd);
248 loginok:
249 D("UNLOCK io(%p)->qel.mut->thr=%lx (%s:%d)", io, (long)io->qel.mut.thr, io->qel.mut.func, io->qel.mut.line);
250 UNLOCK(io->qel.mut, "login");
251 D("UNLOCK ent_mut->thr=%lx (%s:%d)", (long)hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
252 UNLOCK(hit->shf->ent_mut, "login");
253 return 1;
254 }
255
256 #if 0
257 /*() Login an entity using ClientTLS authentication, as evidenced
258 * by a hash of the certificate subject field.
259 * return:: zero on failure, 1 on success */
260
261 /* Called by: */
262 int zxbus_login_subj_hash(struct hi_thr* hit, struct hi_io* io, unsigned long subj_hash)
263 {
264 struct hi_ent* ent;
265 char* p;
266 char* eid;
267 char buf[1024];
268
269 if (!read_all(sizeof(buf), buf, "ClientTLS login", 1,
270 "%s" ZXID_UID_DIR "/%lu/.bs/.at", zxbus_path, subj_hash)) {
271 ERR("Login by ClienTLS failed subj_hash(%lu). No such uid.", subj_hash);
272 return 0;
273 }
274 if (!(eid = strstr(buf, "eid: "))) {
275 ERR("Login by ClienTLS failed subj_hash(%lu). .bs/.at file does not specify eid", subj_hash);
276 return 0;
277 }
278 eid += sizeof("eid: ")-1;
279 if (p = strchr(eid, '\n'))
280 *p = 0;
281
282 LOCK(hit->shf->ent_mut, "subj_hash");
283 D("LOCK ent_mut->thr=%x (%s:%d)", hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
284 if (!(ent = zxbus_load_ent(hit->shf, -1, eid))) {
285 if (hit->shf->anonlogin) {
286 ent = zxbus_new_ent(hit->shf, -1, eid);
287 INFO("Anon login eid(%s)", ent->eid);
288 /* *** consider persisting the newly created account */
289 } else {
290 D("UNLOCK ent_mut->thr=%x (%s:%d)", hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
291 UNLOCK(hit->shf->ent_mut, "subj_hash-fail");
292 ERR("Login account(%s) does not exist and no anon login", eid);
293 return 0;
294 }
295 }
296
297 if (ent->io) {
298 if (ent->io == io) {
299 NEVER("Entity has io already set to current io_%p", io);
300 } else {
301 NEVER("Entity has io already set to different io_%p", ent->io);
302 }
303 }
304
305 ent->io = io;
306 LOCK(io->qel.mut, "subj_hash");
307 D("LOCK io(%x)->qel.mut->thr=%x (%s:%d)", io->qel.mut.thr, io->qel.mut.func, io->qel.mut.line);
308 if (io->ent) {
309 if (io->ent == ent) {
310 NEVER("io has ent already set to current ent_%p", ent);
311 } else {
312 NEVER("io has ent already set to different ent_%p", ent);
313 }
314 }
315 io->ent = ent;
316 D("UNLOCK io(%x)->qel.mut->thr=%x (%s:%d)", io->qel.mut.thr, io->qel.mut.func, io->qel.mut.line);
317 UNLOCK(io->qel.mut, "subj_hash");
318 D("UNLOCK ent_mut->thr=%x (%s:%d)", hit->shf->ent_mut.thr, hit->shf->ent_mut.func, hit->shf->ent_mut.line);
319 UNLOCK(hit->shf->ent_mut, "subj_hash");
320 return 1;
321 }
322 #endif
323
324 /* EOF -- zxbusent.c */
325