1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #include "../test-tools.h"
7 #include <libxml/parser.h>
8 #include <libxml/xpath.h>
9 #include <libxml/xpathInternals.h>
10 #include <libxml/xmlsave.h>
12 static const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
14 /* Used to choose proper name space for message elements.
15 * See _isds_register_namespaces(). */
19 MESSAGE_NS_SIGNED_INCOMING
,
20 MESSAGE_NS_SIGNED_OUTGOING
,
21 MESSAGE_NS_SIGNED_DELIVERY
,
25 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
26 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
27 #define ISDS1_NS "http://isds.czechpoint.cz"
28 #define ISDS_NS "http://isds.czechpoint.cz/v20"
29 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
30 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
31 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
32 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
33 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
34 #define DEPOSIT_NS "urn:uschovnaWSDL"
39 const char *end_point
;
40 const xmlChar
*name_space
;
42 http_error (*function
) (const struct http_connection
*, xmlDocPtr
,
43 xmlXPathContextPtr
, xmlNodePtr
, xmlDocPtr
, xmlNodePtr
,
44 const void *arguments
);
47 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
49 #define ELEMENT_EXISTS(element, allow_multiple) { \
50 xmlXPathObjectPtr result = NULL; \
51 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
52 if (NULL == result) { \
53 error = HTTP_ERROR_SERVER; \
56 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
57 xmlXPathFreeObject(result); \
58 test_asprintf(&message, "Element %s does not exist", element); \
59 error = HTTP_ERROR_CLIENT; \
62 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
63 xmlXPathFreeObject(result); \
64 test_asprintf(&message, "Multiple %s element", element); \
65 error = HTTP_ERROR_CLIENT; \
69 xmlXPathFreeObject(result); \
72 #define EXTRACT_STRING(element, string) { \
73 xmlXPathObjectPtr result = NULL; \
74 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
75 if (NULL == result) { \
76 error = HTTP_ERROR_SERVER; \
79 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
80 if (result->nodesetval->nodeNr > 1) { \
81 xmlXPathFreeObject(result); \
82 test_asprintf(&message, "Multiple %s element", element); \
83 error = HTTP_ERROR_CLIENT; \
87 xmlXPathCastNodeSetToString(result->nodesetval); \
89 xmlXPathFreeObject(result); \
90 error = HTTP_ERROR_SERVER; \
94 xmlXPathFreeObject(result); \
97 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
98 char *string = NULL; \
99 EXTRACT_STRING(element, string); \
101 if (NULL != string) { \
102 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
103 if (NULL == (booleanPtr)) { \
105 error = HTTP_ERROR_SERVER; \
109 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
110 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
112 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
113 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
116 test_asprintf(&message, \
117 "%s value is not valid boolean: %s", \
120 error = HTTP_ERROR_CLIENT; \
129 #define EXTRACT_DATE(element, tmPtr) { \
130 char *string = NULL; \
131 EXTRACT_STRING(element, string); \
132 if (NULL != string) { \
133 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
134 if (NULL == (tmPtr)) { \
136 error = HTTP_ERROR_SERVER; \
139 error = _server_datestring2tm(string, (tmPtr)); \
141 if (error == HTTP_ERROR_CLIENT) { \
142 test_asprintf(&message, "%s value is not a valid date: %s", \
152 /* Following INSERT_* macros expect @error and leave label */
153 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
155 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
156 (xmlChar *) (string)); \
157 if (NULL == node) { \
158 error = HTTP_ERROR_SERVER; \
163 #define INSERT_STRING(parent, element, string) \
164 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
166 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
167 if ((longintPtr)) { \
168 char *buffer = NULL; \
169 /* FIXME: locale sensitive */ \
170 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
171 error = HTTP_ERROR_SERVER; \
174 INSERT_STRING(parent, element, buffer) \
176 } else { INSERT_STRING(parent, element, NULL) } \
179 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
180 if (NULL != (timevalPtr)) { \
181 char *buffer = NULL; \
182 error = timeval2timestring(timevalPtr, &buffer); \
187 INSERT_STRING(parent, element, buffer); \
190 INSERT_STRING(parent, element, NULL); \
194 #define INSERT_TMPTR(parent, element, tmPtr) { \
195 if (NULL != (tmPtr)) { \
196 char *buffer = NULL; \
197 error = tm2datestring(tmPtr, &buffer); \
202 INSERT_STRING(parent, element, buffer); \
205 INSERT_STRING(parent, element, NULL); \
209 #define INSERT_ELEMENT(child, parent, element) \
211 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
212 if (NULL == (child)) { \
213 error = HTTP_ERROR_SERVER; \
219 /* Insert dmStatus or similar subtree
220 * @parent is element to insert to
221 * @dm is true for dmStatus, otherwise dbStatus
222 * @code is status code as string
223 * @message is UTF-8 encoded message
224 * @db_ref_number is optinal reference number propagated if not @dm
225 * @return 0 on success, otherwise non-0. */
226 static http_error
insert_isds_status(xmlNodePtr parent
, _Bool dm
,
227 const xmlChar
*code
, const xmlChar
*message
,
228 const xmlChar
*db_ref_number
) {
229 http_error error
= HTTP_ERROR_SUCCESS
;
232 if (NULL
== code
|| NULL
== message
) {
233 error
= HTTP_ERROR_SERVER
;
237 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
238 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", code
);
239 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", message
);
240 if (!dm
&& NULL
!= db_ref_number
) {
241 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
249 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
250 static http_error
tm2datestring(const struct tm
*time
, char **string
) {
251 if (NULL
== time
|| NULL
== string
) return HTTP_ERROR_SERVER
;
253 if (-1 == test_asprintf(string
, "%d-%02d-%02d",
254 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
255 return HTTP_ERROR_SERVER
;
257 return HTTP_ERROR_SUCCESS
;
261 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
262 * respects the @time microseconds too. */
263 static http_error
timeval2timestring(const struct timeval
*time
,
267 if (!time
|| !string
) return HTTP_ERROR_SERVER
;
269 if (!gmtime_r(&time
->tv_sec
, &broken
)) return HTTP_ERROR_SERVER
;
270 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return HTTP_ERROR_SERVER
;
272 /* TODO: small negative year should be formatted as "-0012". This is not
273 * true for glibc "%04d". We should implement it.
274 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
275 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
276 if (-1 == test_asprintf(string
,
277 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
278 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
279 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
281 return HTTP_ERROR_SERVER
;
283 return HTTP_ERROR_SUCCESS
;
287 /* Implement DummyOperation */
288 static http_error
service_DummyOperation(
289 const struct http_connection
*connection
, const xmlDocPtr soap_request
,
290 xmlXPathContextPtr xpath_ctx
, xmlNodePtr isds_request
,
291 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
292 const void *arguments
) {
293 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
294 BAD_CAST
"Success", NULL
);
298 /* Implement Re-signISDSDocument.
299 * It sends document from request back.
300 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
301 static http_error
service_ResignISDSDocument(
302 const struct http_connection
*connection
,
303 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
304 const xmlNodePtr isds_request
,
305 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
306 const void *arguments
) {
307 http_error error
= HTTP_ERROR_SUCCESS
;
308 const char *code
= "9999";
309 char *message
= NULL
;
310 const struct arguments_DS_Dz_ResignISDSDocument
*configuration
=
311 (const struct arguments_DS_Dz_ResignISDSDocument
*)arguments
;
314 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
315 NULL
== configuration
->status_message
) {
316 error
= HTTP_ERROR_SERVER
;
320 EXTRACT_STRING("isds:dmDoc", data
);
322 message
= strdup("Missing isds:dmDoc");
323 error
= HTTP_ERROR_CLIENT
;
328 /* dmResultDoc is mandatory in response */
329 if (xmlStrcmp(BAD_CAST configuration
->status_code
, BAD_CAST
"0000")) {
333 INSERT_STRING(isds_response
, "dmResultDoc", data
);
335 if (configuration
->valid_to
!= NULL
) {
336 error
= tm2datestring(configuration
->valid_to
, &data
);
338 message
= strdup("Could not format date");
341 INSERT_STRING(isds_response
, "dmValidTo", data
);
344 code
= configuration
->status_code
;
345 message
= strdup(configuration
->status_message
);
348 if (HTTP_ERROR_SERVER
!= error
) {
349 http_error next_error
= insert_isds_status(isds_response
, 1,
350 BAD_CAST code
, BAD_CAST message
, NULL
);
351 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
359 /* Implement EraseMessage.
360 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
361 static http_error
service_EraseMessage(const struct http_connection
*connection
,
362 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
363 const xmlNodePtr isds_request
,
364 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
365 const void *arguments
) {
366 http_error error
= HTTP_ERROR_SUCCESS
;
367 char *code
= "9999", *message
= NULL
;
368 const struct arguments_DS_Dx_EraseMessage
*configuration
=
369 (const struct arguments_DS_Dx_EraseMessage
*)arguments
;
370 char *message_id
= NULL
;
371 _Bool
*incoming
= NULL
;
373 if (NULL
== configuration
|| NULL
== configuration
->message_id
) {
374 error
= HTTP_ERROR_SERVER
;
378 EXTRACT_STRING("isds:dmID", message_id
);
379 if (NULL
== message_id
) {
380 message
= strdup("Missing isds:dmID");
381 error
= HTTP_ERROR_CLIENT
;
384 EXTRACT_BOOLEAN("isds:dmIncoming", incoming
);
385 if (NULL
== incoming
) {
386 message
= strdup("Missing isds:dmIncoming");
387 error
= HTTP_ERROR_CLIENT
;
391 if (xmlStrcmp((const xmlChar
*) configuration
->message_id
,
392 (const xmlChar
*) message_id
)) {
394 message
= strdup("Message is not in the long term storage");
395 error
= HTTP_ERROR_CLIENT
;
398 if (configuration
->incoming
!= *incoming
) {
400 message
= strdup("Message direction mismatches");
401 error
= HTTP_ERROR_CLIENT
;
406 message
= strdup("Success");
408 if (HTTP_ERROR_SERVER
!= error
) {
409 http_error next_error
= insert_isds_status(isds_response
, 1,
410 BAD_CAST code
, BAD_CAST message
, NULL
);
411 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
420 /* Insert list of credit info as XSD:tCiRecord XML tree.
421 * @isds_response is XML node with the response
422 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
423 * subtree will be created. */
424 static http_error
insert_ciRecords(xmlNodePtr isds_response
,
425 const struct server_list
*history
) {
426 http_error error
= HTTP_ERROR_SUCCESS
;
427 xmlNodePtr records
, record
;
429 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
430 if (NULL
== history
) return HTTP_ERROR_SUCCESS
;
432 INSERT_ELEMENT(records
, isds_response
, "ciRecords");
433 for (const struct server_list
*item
= history
; NULL
!= item
;
435 const struct server_credit_event
*event
=
436 (struct server_credit_event
*)item
->data
;
438 INSERT_ELEMENT(record
, records
, "ciRecord");
439 if (NULL
== event
) continue;
441 INSERT_TIMEVALPTR(record
, "ciEventTime", event
->time
);
442 switch(event
->type
) {
443 case SERVER_CREDIT_CHARGED
:
444 INSERT_STRING(record
, "ciEventType", "1");
445 INSERT_STRING(record
, "ciTransID",
446 event
->details
.charged
.transaction
);
448 case SERVER_CREDIT_DISCHARGED
:
449 INSERT_STRING(record
, "ciEventType", "2");
450 INSERT_STRING(record
, "ciTransID",
451 event
->details
.discharged
.transaction
);
453 case SERVER_CREDIT_MESSAGE_SENT
:
454 INSERT_STRING(record
, "ciEventType", "3");
455 INSERT_STRING(record
, "ciRecipientID",
456 event
->details
.message_sent
.recipient
);
457 INSERT_STRING(record
, "ciPDZID",
458 event
->details
.message_sent
.message_id
);
460 case SERVER_CREDIT_STORAGE_SET
:
461 INSERT_STRING(record
, "ciEventType", "4");
462 INSERT_LONGINTPTR(record
, "ciNewCapacity",
463 &event
->details
.storage_set
.new_capacity
);
464 INSERT_TMPTR(record
, "ciNewFrom",
465 event
->details
.storage_set
.new_valid_from
);
466 INSERT_TMPTR(record
, "ciNewTo",
467 event
->details
.storage_set
.new_valid_to
);
468 INSERT_LONGINTPTR(record
, "ciOldCapacity",
469 event
->details
.storage_set
.old_capacity
);
470 INSERT_TMPTR(record
, "ciOldFrom",
471 event
->details
.storage_set
.old_valid_from
);
472 INSERT_TMPTR(record
, "ciOldTo",
473 event
->details
.storage_set
.old_valid_to
);
474 INSERT_STRING(record
, "ciDoneBy",
475 event
->details
.storage_set
.initiator
);
477 case SERVER_CREDIT_EXPIRED
:
478 INSERT_STRING(record
, "ciEventType", "5");
481 error
= HTTP_ERROR_SERVER
;
484 INSERT_LONGINTPTR(record
, "ciCreditChange", &event
->credit_change
);
485 INSERT_LONGINTPTR(record
, "ciCreditAfter", &event
->new_credit
);
494 /* Implement DataBoxCreditInfo.
495 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
496 static http_error
service_DataBoxCreditInfo(
497 const struct http_connection
*connection
,
498 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
499 const xmlNodePtr isds_request
,
500 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
501 const void *arguments
) {
502 http_error error
= HTTP_ERROR_SUCCESS
;
503 const char *code
= "9999";
504 char *message
= NULL
;
505 const struct arguments_DS_df_DataBoxCreditInfo
*configuration
=
506 (const struct arguments_DS_df_DataBoxCreditInfo
*)arguments
;
508 struct tm
*from_date
= NULL
, *to_date
= NULL
;
510 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
511 NULL
== configuration
->status_message
) {
512 error
= HTTP_ERROR_SERVER
;
516 EXTRACT_STRING("isds:dbID", box_id
);
517 if (NULL
== box_id
) {
518 message
= strdup("Missing isds:dbID");
519 error
= HTTP_ERROR_CLIENT
;
522 if (NULL
!= configuration
->box_id
&&
523 xmlStrcmp(BAD_CAST configuration
->box_id
,
526 message
= strdup("Unexpected isds:dbID value");
527 error
= HTTP_ERROR_CLIENT
;
531 ELEMENT_EXISTS("isds:ciFromDate", 0);
532 EXTRACT_DATE("isds:ciFromDate", from_date
);
533 ELEMENT_EXISTS("isds:ciTodate", 0);
534 EXTRACT_DATE("isds:ciTodate", to_date
);
535 /* FIXME: Check for date values */
537 INSERT_LONGINTPTR(isds_response
, "currentCredit",
538 &configuration
->current_credit
);
539 INSERT_STRING(isds_response
, "notifEmail", configuration
->email
);
540 if ((error
= insert_ciRecords(isds_response
, configuration
->history
))) {
544 code
= configuration
->status_code
;
545 message
= strdup(configuration
->status_message
);
547 if (HTTP_ERROR_SERVER
!= error
) {
548 http_error next_error
= insert_isds_status(isds_response
, 0,
549 BAD_CAST code
, BAD_CAST message
, NULL
);
550 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
560 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
561 * @code is output pointer to static string
562 * @pass_message is output pointer to auto-allocated string
563 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
564 static http_error
check_passwd(
565 const char *username
, const char *current_password
,
566 xmlXPathContextPtr xpath_ctx
,
567 char **code
, char **pass_message
) {
568 http_error error
= HTTP_ERROR_SUCCESS
;
569 char *message
= NULL
;
570 char *old_password
= NULL
, *new_password
= NULL
;
573 if (NULL
== username
|| NULL
== current_password
||
574 NULL
== code
|| NULL
== pass_message
) {
575 error
= HTTP_ERROR_SERVER
;
583 EXTRACT_STRING("isds:dbOldPassword", old_password
);
584 if (NULL
== old_password
) {
585 message
= strdup("Empty isds:dbOldPassword");
586 error
= HTTP_ERROR_CLIENT
;
589 EXTRACT_STRING("isds:dbNewPassword", new_password
);
590 if (NULL
== new_password
) {
591 message
= strdup("Empty isds:dbOldPassword");
592 error
= HTTP_ERROR_CLIENT
;
596 /* Check defined cases */
597 if (strcmp(current_password
, old_password
)) {
599 message
= strdup("Bad current password");
600 error
= HTTP_ERROR_CLIENT
;
604 length
= strlen(new_password
);
606 if (length
< 8 || length
> 32) {
608 message
= strdup("Too short or too long");
609 error
= HTTP_ERROR_CLIENT
;
614 const char lower
[] = "abcdefghijklmnopqrstuvwxyz";
615 const char upper
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
616 const char digit
[] = "0123456789";
617 const char special
[] = "!#$%&()*+,-.:=?@[]_{}|~";
618 _Bool has_lower
= 0, has_upper
= 0, has_digit
=0;
620 for (int i
= 0; i
< length
; i
++) {
621 if (NULL
!= strchr(lower
, new_password
[i
]))
623 else if (NULL
!= strchr(upper
, new_password
[i
]))
625 else if (NULL
!= strchr(digit
, new_password
[i
]))
627 else if (NULL
== strchr(special
, new_password
[i
])) {
629 message
= strdup("Password contains forbidden character");
630 error
= HTTP_ERROR_CLIENT
;
635 if (!has_lower
|| !has_upper
|| !has_digit
) {
637 message
= strdup("Password does not contain lower cased letter, "
638 "upper cased letter and a digit");
639 error
= HTTP_ERROR_CLIENT
;
644 if (!strcmp(old_password
, new_password
)) {
646 message
= strdup("New password same as current one");
647 error
= HTTP_ERROR_CLIENT
;
651 if (NULL
!= strstr(new_password
, username
)) {
653 message
= strdup("New password contains user ID");
654 error
= HTTP_ERROR_CLIENT
;
658 for (int i
= 0; i
< length
- 2; i
++) {
659 if (new_password
[i
] == new_password
[i
+1] &&
660 new_password
[i
] == new_password
[i
+2]) {
662 message
= strdup("Password contains sequence "
663 "of three identical characters");
664 error
= HTTP_ERROR_CLIENT
;
670 const char *forbidden_prefix
[] = { "qwert", "asdgf", "12345" };
671 for (int i
= 0; i
< sizeof(forbidden_prefix
)/sizeof(*forbidden_prefix
);
673 if (!strncmp(new_password
, forbidden_prefix
[i
],
674 strlen(forbidden_prefix
[i
]))) {
676 message
= strdup("Password has forbidden prefix");
677 error
= HTTP_ERROR_CLIENT
;
684 message
= strdup("Success");
688 *pass_message
= message
;
693 /* Implement ChangeISDSPassword.
694 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
695 static http_error
service_ChangeISDSPassword(
696 const struct http_connection
*connection
,
697 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
698 const xmlNodePtr isds_request
,
699 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
700 const void *arguments
) {
701 http_error error
= HTTP_ERROR_SUCCESS
;
702 char *code
= "9999", *message
= NULL
;
703 const struct arguments_DS_DsManage_ChangeISDSPassword
*configuration
=
704 (const struct arguments_DS_DsManage_ChangeISDSPassword
*)arguments
;
706 if (NULL
== configuration
|| NULL
== configuration
->username
||
707 NULL
== configuration
->current_password
) {
708 error
= HTTP_ERROR_SERVER
;
712 /* Check for common password rules */
713 error
= check_passwd(
714 configuration
->username
, configuration
->current_password
,
715 xpath_ctx
, &code
, &message
);
718 if (HTTP_ERROR_SERVER
!= error
) {
719 http_error next_error
= insert_isds_status(isds_response
, 0,
720 BAD_CAST code
, BAD_CAST message
, NULL
);
721 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
728 /* Implement ChangePasswordOTP.
729 * @arguments is pointer to struct
730 * arguments_asws_changePassword_ChangePasswordOTP */
731 static http_error
service_ChangePasswordOTP(
732 const struct http_connection
*connection
,
733 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
734 const xmlNodePtr isds_request
,
735 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
736 const void *arguments
) {
737 http_error error
= HTTP_ERROR_SUCCESS
;
738 char *code
= "9999", *message
= NULL
;
739 const struct arguments_asws_changePassword_ChangePasswordOTP
*configuration
740 = (const struct arguments_asws_changePassword_ChangePasswordOTP
*)
744 if (NULL
== configuration
|| NULL
== configuration
->username
||
745 NULL
== configuration
->current_password
) {
746 error
= HTTP_ERROR_SERVER
;
750 /* Chek for OTP method */
751 EXTRACT_STRING("isds:dbOTPType", method
);
752 if (NULL
== method
) {
753 message
= strdup("Empty isds:dbOTPType");
754 error
= HTTP_ERROR_CLIENT
;
757 if ((configuration
->method
== AUTH_OTP_HMAC
&& strcmp(method
, "HOTP")) ||
758 (configuration
->method
== AUTH_OTP_TIME
&& strcmp(method
, "TOTP"))) {
759 message
= strdup("isds:dbOTPType does not match OTP method");
760 error
= HTTP_ERROR_CLIENT
;
764 /* Check for common password rules */
765 error
= check_passwd(
766 configuration
->username
, configuration
->current_password
,
767 xpath_ctx
, &code
, &message
);
770 if (HTTP_ERROR_SERVER
!= error
) {
771 http_error next_error
= insert_isds_status(isds_response
, 0,
772 BAD_CAST code
, BAD_CAST message
,
773 BAD_CAST configuration
->reference_number
);
774 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
782 /* Implement SendSMSCode.
783 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
784 static http_error
service_SendSMSCode(
785 const struct http_connection
*connection
,
786 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
787 const xmlNodePtr isds_request
,
788 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
789 const void *arguments
) {
790 const struct arguments_asws_changePassword_SendSMSCode
*configuration
791 = (const struct arguments_asws_changePassword_SendSMSCode
*)
794 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
795 NULL
== configuration
->status_message
) {
796 return HTTP_ERROR_SERVER
;
799 return insert_isds_status(isds_response
, 0,
800 BAD_CAST configuration
->status_code
,
801 BAD_CAST configuration
->status_message
,
802 BAD_CAST configuration
->reference_number
);
806 /* List of implemented services */
807 static struct service services
[] = {
808 { SERVICE_DS_Dz_DummyOperation
,
809 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"DummyOperation",
810 service_DummyOperation
},
811 { SERVICE_DS_Dz_ResignISDSDocument
,
812 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"Re-signISDSDocument",
813 service_ResignISDSDocument
},
814 { SERVICE_DS_df_DataBoxCreditInfo
,
815 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"DataBoxCreditInfo",
816 service_DataBoxCreditInfo
},
817 { SERVICE_DS_DsManage_ChangeISDSPassword
,
818 "DS/DsManage", BAD_CAST ISDS_NS
, BAD_CAST
"ChangeISDSPassword",
819 service_ChangeISDSPassword
},
820 { SERVICE_DS_Dx_EraseMessage
,
821 "DS/dx", BAD_CAST ISDS_NS
, BAD_CAST
"EraseMessage",
822 service_EraseMessage
},
823 { SERVICE_asws_changePassword_ChangePasswordOTP
,
824 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"ChangePasswordOTP",
825 service_ChangePasswordOTP
},
826 { SERVICE_asws_changePassword_SendSMSCode
,
827 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"SendSMSCode",
828 service_SendSMSCode
},
832 /* Makes known all relevant namespaces to given XPath context
833 * @xpath_ctx is XPath context
834 * @otp_ns selects name space for the request and response know as "isds".
835 * Use true for OTP-authenticated password change services, otherwise false.
836 * @message_ns selects proper message name space. Unsigned and signed
837 * messages and delivery info's differ in prefix and URI.
838 * @return 0 in success, otherwise not 0. */
839 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
840 const _Bool otp_ns
, const message_ns_type message_ns
) {
841 const xmlChar
*service_namespace
= NULL
;
842 const xmlChar
*message_namespace
= NULL
;
844 if (!xpath_ctx
) return -1;
847 service_namespace
= BAD_CAST OISDS_NS
;
849 service_namespace
= BAD_CAST ISDS_NS
;
854 message_namespace
= BAD_CAST ISDS1_NS
; break;
855 case MESSAGE_NS_UNSIGNED
:
856 message_namespace
= BAD_CAST ISDS_NS
; break;
857 case MESSAGE_NS_SIGNED_INCOMING
:
858 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
859 case MESSAGE_NS_SIGNED_OUTGOING
:
860 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
861 case MESSAGE_NS_SIGNED_DELIVERY
:
862 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
867 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
869 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", service_namespace
))
871 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
873 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
875 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
881 /* Parse soap request, pass it to service endpoint and respond to it.
882 * It sends final HTTP response. */
883 void soap(const struct http_connection
*connection
,
884 const struct service_configuration
*configuration
,
885 const void *request
, size_t request_length
, const char *end_point
) {
886 xmlDocPtr request_doc
= NULL
;
887 xmlXPathContextPtr xpath_ctx
= NULL
;
888 xmlXPathObjectPtr request_soap_body
= NULL
;
889 xmlNodePtr isds_request
= NULL
; /* pointer only */
890 _Bool service_handled
= 0, service_passed
= 0;
891 xmlDocPtr response_doc
= NULL
;
892 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
893 isds_response
= NULL
;
894 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
895 char *response_name
= NULL
;
896 xmlBufferPtr http_response_body
= NULL
;
897 xmlSaveCtxtPtr save_ctx
= NULL
;
900 if (NULL
== configuration
) {
901 http_send_response_500(connection
,
902 "Second argument of soap() is NULL");
906 if (NULL
== request
|| request_length
== 0) {
907 http_send_response_400(connection
, "Client sent empty body");
911 request_doc
= xmlParseMemory(request
, request_length
);
912 if (NULL
== request_doc
) {
913 http_send_response_400(connection
, "Client sent invalid XML document");
917 xpath_ctx
= xmlXPathNewContext(request_doc
);
918 if (NULL
== xpath_ctx
) {
919 xmlFreeDoc(request_doc
);
920 http_send_response_500(connection
, "Could not create XPath context");
924 if (register_namespaces(xpath_ctx
, 0, MESSAGE_NS_UNSIGNED
)) {
925 xmlXPathFreeContext(xpath_ctx
);
926 xmlFreeDoc(request_doc
);
927 http_send_response_500(connection
,
928 "Could not register name spaces to the XPath context");
933 request_soap_body
= xmlXPathEvalExpression(
934 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
935 if (NULL
== request_soap_body
) {
936 xmlXPathFreeContext(xpath_ctx
);
937 xmlFreeDoc(request_doc
);
938 http_send_response_400(connection
, "Client sent invalid SOAP request");
941 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
942 xmlXPathFreeObject(request_soap_body
);
943 xmlXPathFreeContext(xpath_ctx
);
944 xmlFreeDoc(request_doc
);
945 http_send_response_400(connection
,
946 "SOAP request does not contain SOAP Body element");
949 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
950 xmlXPathFreeObject(request_soap_body
);
951 xmlXPathFreeContext(xpath_ctx
);
952 xmlFreeDoc(request_doc
);
953 http_send_response_400(connection
,
954 "SOAP response has more than one Body element");
957 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
958 if (isds_request
->next
!= NULL
) {
959 xmlXPathFreeObject(request_soap_body
);
960 xmlXPathFreeContext(xpath_ctx
);
961 xmlFreeDoc(request_doc
);
962 http_send_response_400(connection
, "SOAP body has more than one child");
965 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
966 NULL
== isds_request
->ns
->href
) {
967 xmlXPathFreeObject(request_soap_body
);
968 xmlXPathFreeContext(xpath_ctx
);
969 xmlFreeDoc(request_doc
);
970 http_send_response_400(connection
,
971 "SOAP body does not contain a name-space-qualified element");
975 /* Build SOAP response envelope */
976 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
978 http_send_response_500(connection
,
979 "Could not build SOAP response document");
982 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
983 if (!response_soap_envelope
) {
984 http_send_response_500(connection
,
985 "Could not build SOAP response envelope");
988 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
989 /* Only this way we get namespace definition as @xmlns:soap,
990 * otherwise we get namespace prefix without definition */
991 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
992 if(NULL
== soap_ns
) {
993 http_send_response_500(connection
, "Could not create SOAP name space");
996 xmlSetNs(response_soap_envelope
, soap_ns
);
997 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
998 BAD_CAST
"Body", NULL
);
999 if (!response_soap_body
) {
1000 http_send_response_500(connection
,
1001 "Could not add Body to SOAP response envelope");
1004 /* Append ISDS response element */
1005 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
1007 http_send_response_500(connection
,
1008 "Could not buld ISDS resposne element name");
1011 isds_response
= xmlNewChild(response_soap_body
, NULL
,
1012 BAD_CAST response_name
, NULL
);
1013 free(response_name
);
1014 if (NULL
== isds_response
) {
1015 http_send_response_500(connection
,
1016 "Could not add ISDS response element to SOAP response body");
1019 isds_ns
= xmlNewNs(isds_response
, isds_request
->ns
->href
, NULL
);
1020 if(NULL
== isds_ns
) {
1021 http_send_response_500(connection
,
1022 "Could not create a name space for the response body");
1025 xmlSetNs(isds_response
, isds_ns
);
1027 /* Dispatch request to service */
1028 for (int i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
1029 if (!strcmp(services
[i
].end_point
, end_point
) &&
1030 !xmlStrcmp(services
[i
].name_space
, isds_request
->ns
->href
) &&
1031 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
1032 /* Check if the configuration is enabled and find configuration */
1033 for (const struct service_configuration
*service
= configuration
;
1034 service
->name
!= SERVICE_END
; service
++) {
1035 if (service
->name
== services
[i
].id
) {
1036 service_handled
= 1;
1037 if (!xmlStrcmp(services
[i
].name_space
, BAD_CAST OISDS_NS
)) {
1038 /* Alias "isds" XPath identifier to OISDS_NS */
1039 if (register_namespaces(xpath_ctx
, 1,
1040 MESSAGE_NS_UNSIGNED
)) {
1041 http_send_response_500(connection
,
1042 "Could not register name spaces to the "
1047 xpath_ctx
->node
= isds_request
;
1048 if (HTTP_ERROR_SERVER
!= services
[i
].function(connection
,
1049 request_doc
, xpath_ctx
, isds_request
,
1050 response_doc
, isds_response
,
1051 service
->arguments
)) {
1054 http_send_response_500(connection
,
1055 "Internal server error while processing "
1065 if (service_passed
) {
1066 /* Serialize the SOAP response */
1067 http_response_body
= xmlBufferCreate();
1068 if (NULL
== http_response_body
) {
1069 http_send_response_500(connection
,
1070 "Could not create xmlBuffer for response serialization");
1073 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1074 * XML document transport as it adds text nodes (indentiation) between
1076 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
1077 if (NULL
== save_ctx
) {
1078 http_send_response_500(connection
, "Could not create XML serializer");
1081 /* XXX: According LibXML documentation, this function does not return
1082 * meaningful value yet */
1083 xmlSaveDoc(save_ctx
, response_doc
);
1084 if (-1 == xmlSaveFlush(save_ctx
)) {
1085 http_send_response_500(connection
,
1086 "Could not serialize SOAP response");
1090 http_send_response_200(connection
, http_response_body
->content
,
1091 http_response_body
->use
, soap_mime_type
);
1095 xmlSaveClose(save_ctx
);
1096 xmlBufferFree(http_response_body
);
1098 xmlFreeDoc(response_doc
);
1100 xmlXPathFreeObject(request_soap_body
);
1101 xmlXPathFreeContext(xpath_ctx
);
1102 xmlFreeDoc(request_doc
);
1104 if (!service_handled
) {
1105 http_send_response_500(connection
,
1106 "Requested ISDS service not implemented");