8afd6ff0967c61ff8f24d7dfc1c21ba7fcd5c361
[strongswan.git] / src / libcharon / plugins / tnc_ifmap / tnc_ifmap_soap_msg.c
1 /*
2 * Copyright (C) 2013 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE /* for asprintf() */
17
18 #include "tnc_ifmap_soap_msg.h"
19
20 #include <utils/debug.h>
21 #include <utils/lexparser.h>
22
23 #include <stdio.h>
24
25 #define SOAP_NS "http://www.w3.org/2003/05/soap-envelope"
26
27 typedef struct private_tnc_ifmap_soap_msg_t private_tnc_ifmap_soap_msg_t;
28
29 /**
30 * Private data of an tnc_ifmap_soap_msg_t object.
31 */
32 struct private_tnc_ifmap_soap_msg_t {
33
34 /**
35 * Public tnc_ifmap_soap_msg_t interface.
36 */
37 tnc_ifmap_soap_msg_t public;
38
39 /**
40 * HTTPS Server URI with https:// prefix removed
41 */
42 char *uri;
43
44 /**
45 * Optional base64-encoded username:password for HTTP Basic Authentication
46 */
47 chunk_t user_pass;
48
49 /**
50 * TLS Socket
51 */
52 tls_socket_t *tls;
53
54 /**
55 * XML Document
56 */
57 xmlDocPtr doc;
58
59 };
60
61 /**
62 * Send HTTP POST request and receive HTTP response
63 */
64 static bool http_post(private_tnc_ifmap_soap_msg_t *this, chunk_t out,
65 chunk_t *in)
66 {
67 char *host, *path, *request, buf[2048];
68 chunk_t line, http, parameter;
69 int len, code, content_len = 0;
70
71 /* Duplicate host[/path] string since we are going to manipulate it */
72 len = strlen(this->uri) + 2;
73 host = malloc(len);
74 memset(host, '\0', len);
75 strcpy(host, this->uri);
76
77 /* Extract appended path or set to root */
78 path = strchr(host, '/');
79 if (!path)
80 {
81 path = host + len - 2;
82 *path = '/';
83 }
84
85 /* Use Basic Authentication? */
86 if (this->user_pass.len)
87 {
88 snprintf(buf, sizeof(buf), "Authorization: Basic %.*s\r\n",
89 this->user_pass.len, this->user_pass.ptr);
90 }
91 else
92 {
93 *buf = '\0';
94 }
95
96 /* Write HTTP POST request */
97 len = asprintf(&request,
98 "POST %s HTTP/1.1\r\n"
99 "Host: %.*s\r\n"
100 "%s"
101 "Content-Type: application/soap+xml;charset=utf-8\r\n"
102 "Content-Length: %d\r\n"
103 "\r\n"
104 "%.*s", path, (path-host), host, buf, out.len, out.len, out.ptr);
105 free(host);
106
107 if (len == -1)
108 {
109 return FALSE;
110 }
111 http = chunk_create(request, len);
112 DBG3(DBG_TLS, "%B", &http);
113
114 this->tls->write(this->tls, request, len);
115 free(request);
116
117 /* Read HTTP response */
118 len = this->tls->read(this->tls, buf, sizeof(buf), TRUE);
119 if (len == -1)
120 {
121 return FALSE;
122 }
123 *in = chunk_create(buf, len);
124
125 /* Process HTTP protocol version */
126 if (!fetchline(in, &line) || !extract_token(&http, ' ', &line) ||
127 !match("HTTP/1.1", &http) || sscanf(line.ptr, "%d", &code) != 1)
128 {
129 DBG1(DBG_TNC, "malformed http response header");
130 return FALSE;
131 }
132 if (code != 200)
133 {
134 DBG1(DBG_TNC, "http response returns error code %d", code);
135 return FALSE;
136 }
137
138 /* Process HTTP header line by line until the HTTP body is reached */
139 while (fetchline(in, &line))
140 {
141 if (line.len == 0)
142 {
143 break;
144 }
145
146 if (extract_token(&parameter, ':', &line) &&
147 match("Content-Length", &parameter) &&
148 sscanf(line.ptr, "%d", &len) == 1)
149 {
150 content_len = len;
151 }
152 }
153
154 /* Found Content-Length parameter and check size of HTTP body */
155 if (content_len)
156 {
157 if (content_len > in->len)
158 {
159 DBG1(DBG_TNC, "http body is smaller than content length");
160 return FALSE;
161 }
162 in->len = content_len;
163 }
164 *in = chunk_clone(*in);
165
166 return TRUE;
167 }
168
169 /**
170 * Find a child node with a given name
171 */
172 static xmlNodePtr find_child(xmlNodePtr parent, const xmlChar* name)
173 {
174 xmlNodePtr child;
175
176 child = parent->xmlChildrenNode;
177 while (child)
178 {
179 if (xmlStrcmp(child->name, name) == 0)
180 {
181 return child;
182 }
183 child = child->next;
184 }
185
186 DBG1(DBG_TNC, "child node \"%s\" not found", name);
187 return NULL;
188 }
189
190 METHOD(tnc_ifmap_soap_msg_t, post, bool,
191 private_tnc_ifmap_soap_msg_t *this, xmlNodePtr request, char *result_name,
192 xmlNodePtr *result)
193 {
194 xmlDocPtr doc;
195 xmlNodePtr env, body, cur, response;
196 xmlNsPtr ns;
197 xmlChar *xml, *errorCode, *errorString;
198 int len;
199 chunk_t in, out;
200
201 DBG2(DBG_TNC, "sending ifmap %s", request->name);
202
203 /* Generate XML Document containing SOAP Envelope */
204 doc = xmlNewDoc("1.0");
205 env =xmlNewNode(NULL, "Envelope");
206 ns = xmlNewNs(env, SOAP_NS, "env");
207 xmlSetNs(env, ns);
208 xmlDocSetRootElement(doc, env);
209
210 /* Add SOAP Body containing IF-MAP request */
211 body = xmlNewNode(ns, "Body");
212 xmlAddChild(body, request);
213 xmlAddChild(env, body);
214
215 /* Convert XML Document into a character string */
216 xmlDocDumpFormatMemory(doc, &xml, &len, 1);
217 xmlFreeDoc(doc);
218 DBG3(DBG_TNC, "%.*s", len, xml);
219 out = chunk_create(xml, len);
220
221 /* Send SOAP-XML request via HTTP POST */
222 if (!http_post(this, out, &in))
223 {
224 xmlFree(xml);
225 return FALSE;
226 }
227 xmlFree(xml);
228
229 DBG3(DBG_TNC, "%B", &in);
230 this->doc = xmlParseMemory(in.ptr, in.len);
231 free(in.ptr);
232
233 if (!this->doc)
234 {
235 DBG1(DBG_TNC, "failed to parse XML message");
236 return FALSE;
237 }
238
239 /* check out XML document */
240 cur = xmlDocGetRootElement(this->doc);
241 if (!cur)
242 {
243 DBG1(DBG_TNC, "empty XML message");
244 return FALSE;
245 }
246
247 /* get XML Document type is a SOAP Envelope */
248 if (xmlStrcmp(cur->name, "Envelope"))
249 {
250 DBG1(DBG_TNC, "XML message does not contain a SOAP Envelope");
251 return FALSE;
252 }
253
254 /* get SOAP Body */
255 cur = find_child(cur, "Body");
256 if (!cur)
257 {
258 return FALSE;
259 }
260
261 /* get IF-MAP response */
262 response = find_child(cur, "response");
263 if (!response)
264 {
265 return FALSE;
266 }
267
268 /* get IF-MAP result */
269 cur = find_child(response, result_name);
270 if (!cur)
271 {
272 cur = find_child(response, "errorResult");
273 if (cur)
274 {
275 DBG1(DBG_TNC, "received errorResult");
276
277 errorCode = xmlGetProp(cur, "errorCode");
278 if (errorCode)
279 {
280 DBG1(DBG_TNC, " %s", errorCode);
281 xmlFree(errorCode);
282 }
283
284 cur = find_child(cur, "errorString");
285 if (cur)
286 {
287 errorString = xmlNodeGetContent(cur);
288 if (errorString)
289 {
290 DBG1(DBG_TNC, " %s", errorString);
291 xmlFree(errorString);
292 }
293 }
294 }
295 return FALSE;
296 }
297
298 if (result)
299 {
300 *result = cur;
301 }
302 return TRUE;
303 }
304
305 METHOD(tnc_ifmap_soap_msg_t, destroy, void,
306 private_tnc_ifmap_soap_msg_t *this)
307 {
308 if (this->doc)
309 {
310 xmlFreeDoc(this->doc);
311 }
312 free(this);
313 }
314
315 /**
316 * See header
317 */
318 tnc_ifmap_soap_msg_t *tnc_ifmap_soap_msg_create(char *uri, chunk_t user_pass,
319 tls_socket_t *tls)
320 {
321 private_tnc_ifmap_soap_msg_t *this;
322
323 INIT(this,
324 .public = {
325 .post = _post,
326 .destroy = _destroy,
327 },
328 .uri = uri,
329 .user_pass = user_pass,
330 .tls = tls,
331 );
332
333 return &this->public;
334 }
335