1/*
2 * Policy fetching for Secure Streams
3 *
4 * libwebsockets - small server side websockets and web server implementation
5 *
6 * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#include <private-lib-core.h>
28
29typedef struct ss_fetch_policy {
30	struct lws_ss_handle 	*ss;
31	void			*opaque_data;
32	/* ... application specific state ... */
33
34	lws_sorted_usec_list_t	sul;
35
36	uint8_t			partway;
37} ss_fetch_policy_t;
38
39/* secure streams payload interface */
40
41static lws_ss_state_return_t
42ss_fetch_policy_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
43{
44	ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj;
45	struct lws_context *context = (struct lws_context *)m->opaque_data;
46
47	if (flags & LWSSS_FLAG_SOM) {
48		if (lws_ss_policy_parse_begin(context, 0))
49			return LWSSSSRET_OK;
50		m->partway = 1;
51	}
52
53	if (len && lws_ss_policy_parse(context, buf, len) < 0)
54		return LWSSSSRET_OK;
55
56	if (flags & LWSSS_FLAG_EOM)
57		m->partway = 2;
58
59	return LWSSSSRET_OK;
60}
61
62static lws_ss_state_return_t
63ss_fetch_policy_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
64		   size_t *len, int *flags)
65{
66	return LWSSSSRET_TX_DONT_SEND;
67}
68
69static void
70policy_set(lws_sorted_usec_list_t *sul)
71{
72	ss_fetch_policy_t *m = lws_container_of(sul, ss_fetch_policy_t, sul);
73	struct lws_context *context = (struct lws_context *)m->opaque_data;
74
75	/*
76	 * We get called if the policy parse was successful, just after the
77	 * ss connection close that was using the vhost from the old policy
78	 */
79
80	lws_ss_destroy(&m->ss);
81
82	if (lws_ss_policy_set(context, "updated"))
83		lwsl_err("%s: policy set failed\n", __func__);
84	else {
85		context->policy_updated = 1;
86#if defined(LWS_WITH_SYS_STATE)
87		lws_state_transition_steps(&context->mgr_system,
88					   LWS_SYSTATE_OPERATIONAL);
89#endif
90	}
91}
92
93static lws_ss_state_return_t
94ss_fetch_policy_state(void *userobj, void *sh, lws_ss_constate_t state,
95		      lws_ss_tx_ordinal_t ack)
96{
97	ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj;
98	struct lws_context *context = (struct lws_context *)m->opaque_data;
99
100	lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state),
101		  (unsigned int)ack);
102
103	switch (state) {
104	case LWSSSCS_CREATING:
105		return lws_ss_request_tx(m->ss);
106
107	case LWSSSCS_CONNECTING:
108		break;
109
110	case LWSSSCS_QOS_ACK_REMOTE:
111		switch (m->partway) {
112		case 2:
113			lws_sul_schedule(context, 0, &m->sul, policy_set, 1);
114			m->partway = 0;
115			break;
116		}
117		break;
118
119	case LWSSSCS_DISCONNECTED:
120		if (m->partway == 1) {
121			lws_ss_policy_parse_abandon(context);
122			break;
123		}
124		m->partway = 0;
125		break;
126
127	default:
128		break;
129	}
130
131	return LWSSSSRET_OK;
132}
133
134int
135lws_ss_sys_fetch_policy(struct lws_context *context)
136{
137	lws_ss_info_t ssi;
138
139	if (context->hss_fetch_policy) /* already exists */
140		return 0;
141
142	/* We're making an outgoing secure stream ourselves */
143
144	memset(&ssi, 0, sizeof(ssi));
145	ssi.handle_offset	    = offsetof(ss_fetch_policy_t, ss);
146	ssi.opaque_user_data_offset = offsetof(ss_fetch_policy_t, opaque_data);
147	ssi.rx			    = ss_fetch_policy_rx;
148	ssi.tx			    = ss_fetch_policy_tx;
149	ssi.state		    = ss_fetch_policy_state;
150	ssi.user_alloc		    = sizeof(ss_fetch_policy_t);
151	ssi.streamtype		    = "fetch_policy";
152
153	if (lws_ss_create(context, 0, &ssi, context, &context->hss_fetch_policy,
154			  NULL, NULL)) {
155		/*
156		 * If there's no fetch_policy streamtype, it can just be we're
157		 * running on a proxied client with no policy of its own,
158		 * it's OK.
159		 */
160		lwsl_info("%s: Policy fetch ss failed (stub policy?)\n", __func__);
161
162		return 0;
163	}
164
165	lwsl_info("%s: policy fetching ongoing\n", __func__);
166
167	/* fetching it is ongoing */
168
169	return 1;
170}
171