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