diff options
author | Markus Voggenreiter <Markus.Voggenreiter@tum.de> | 2019-11-27 12:30:49 +0100 |
---|---|---|
committer | Schanzenbach, Martin <mschanzenbach@posteo.de> | 2020-01-13 13:31:05 +0100 |
commit | cb08bbcbefc98afe6b8c7600bb0dfb1241343cff (patch) | |
tree | ec801cb4e772bd245ca8d22f19bc751ca67d2737 /src/reclaim | |
parent | c0fce9ca75973a646f80372fcc08c059818ba548 (diff) | |
download | gnunet-cb08bbcbefc98afe6b8c7600bb0dfb1241343cff.tar.gz gnunet-cb08bbcbefc98afe6b8c7600bb0dfb1241343cff.zip |
Basic Functionality Implemented
Diffstat (limited to 'src/reclaim')
-rw-r--r-- | src/reclaim/gnunet-service-reclaim.c | 71 | ||||
-rw-r--r-- | src/reclaim/gnunet-service-reclaim_tickets.c | 260 | ||||
-rw-r--r-- | src/reclaim/json_reclaim.c | 7 | ||||
-rw-r--r-- | src/reclaim/oidc_helper.c | 126 | ||||
-rw-r--r-- | src/reclaim/oidc_helper.h | 2 | ||||
-rw-r--r-- | src/reclaim/plugin_gnsrecord_reclaim.c | 2 | ||||
-rw-r--r-- | src/reclaim/plugin_rest_openid_connect.c | 167 | ||||
-rw-r--r-- | src/reclaim/plugin_rest_reclaim.c | 5 | ||||
-rw-r--r-- | src/reclaim/reclaim_api.c | 18 |
9 files changed, 540 insertions, 118 deletions
diff --git a/src/reclaim/gnunet-service-reclaim.c b/src/reclaim/gnunet-service-reclaim.c index fcbd9413f..8b7557090 100644 --- a/src/reclaim/gnunet-service-reclaim.c +++ b/src/reclaim/gnunet-service-reclaim.c | |||
@@ -1251,6 +1251,31 @@ reference_store_cont (void *cls, int32_t success, const char *emsg) | |||
1251 | } | 1251 | } |
1252 | 1252 | ||
1253 | /** | 1253 | /** |
1254 | * Send a reference error response | ||
1255 | * | ||
1256 | * @param ash our attribute store handle | ||
1257 | * @param success the success status | ||
1258 | */ | ||
1259 | static void | ||
1260 | send_ref_error (struct AttributeStoreHandle *ash) | ||
1261 | { | ||
1262 | struct GNUNET_MQ_Envelope *env; | ||
1263 | struct SuccessResultMessage *acr_msg; | ||
1264 | |||
1265 | ash->ns_qe = NULL; | ||
1266 | GNUNET_CONTAINER_DLL_remove (ash->client->store_op_head, | ||
1267 | ash->client->store_op_tail, | ||
1268 | ash); | ||
1269 | |||
1270 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending SUCCESS_RESPONSE message\n"); | ||
1271 | env = GNUNET_MQ_msg (acr_msg, GNUNET_MESSAGE_TYPE_RECLAIM_SUCCESS_RESPONSE); | ||
1272 | acr_msg->id = htonl (ash->r_id); | ||
1273 | acr_msg->op_result = htonl (GNUNET_SYSERR); | ||
1274 | GNUNET_MQ_send (ash->client->mq, env); | ||
1275 | cleanup_as_handle (ash); | ||
1276 | } | ||
1277 | |||
1278 | /** | ||
1254 | * Check for existing record before storing reference | 1279 | * Check for existing record before storing reference |
1255 | * | 1280 | * |
1256 | * @param cls our attribute store handle | 1281 | * @param cls our attribute store handle |
@@ -1272,33 +1297,46 @@ ref_add_cb (void *cls, | |||
1272 | buf_size = GNUNET_RECLAIM_ATTESTATION_REF_serialize_get_size (ash->reference); | 1297 | buf_size = GNUNET_RECLAIM_ATTESTATION_REF_serialize_get_size (ash->reference); |
1273 | buf = GNUNET_malloc (buf_size); | 1298 | buf = GNUNET_malloc (buf_size); |
1274 | GNUNET_RECLAIM_ATTESTATION_REF_serialize (ash->reference, buf); | 1299 | GNUNET_RECLAIM_ATTESTATION_REF_serialize (ash->reference, buf); |
1300 | struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *ref; | ||
1301 | char *data_tmp; | ||
1275 | if (0 == rd_count ) | 1302 | if (0 == rd_count ) |
1276 | { | 1303 | { |
1277 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 1304 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
1278 | "Failed to find Attestation entry for Attestation reference\n"); | 1305 | "Failed to find Attestation entry for Attestation reference\n"); |
1279 | cleanup_as_handle (ash); | 1306 | send_ref_error (ash); |
1280 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1281 | return; | 1307 | return; |
1282 | } | 1308 | } |
1283 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR != rd[0].record_type) | 1309 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR != rd[0].record_type) |
1284 | { | 1310 | { |
1285 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 1311 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
1286 | "Intended Reference storage location is not an attestation\n"); | 1312 | "Intended Reference storage location is not an attestation\n"); |
1287 | cleanup_as_handle (ash); | 1313 | send_ref_error (ash); |
1288 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1289 | return; | 1314 | return; |
1290 | } | 1315 | } |
1291 | struct GNUNET_GNSRECORD_Data rd_new[rd_count + 1]; | 1316 | struct GNUNET_GNSRECORD_Data rd_new[rd_count + 1]; |
1292 | int i; | 1317 | int i; |
1293 | for (i = 0; i<rd_count; i++) | 1318 | for (i = 0; i<rd_count; i++) |
1294 | { | 1319 | { |
1320 | data_tmp = GNUNET_malloc (rd[i].data_size); | ||
1321 | GNUNET_memcpy (data_tmp, rd[i].data, rd[i].data_size); | ||
1322 | ref = GNUNET_RECLAIM_ATTESTATION_REF_deserialize (data_tmp, htons ( | ||
1323 | rd[i].data_size)); | ||
1295 | rd_new[i] = rd[i]; | 1324 | rd_new[i] = rd[i]; |
1325 | if ((strcmp (ash->reference->name,ref->name) == 0)&& | ||
1326 | (strcmp (ash->reference->reference_value,ref->reference_value)==0) ) | ||
1327 | { | ||
1328 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1329 | "Reference already stored\n"); | ||
1330 | reference_store_cont (ash,GNUNET_OK, NULL); | ||
1331 | return; | ||
1332 | } | ||
1296 | } | 1333 | } |
1297 | rd_new[rd_count].data_size = buf_size; | 1334 | rd_new[rd_count].data_size = buf_size; |
1298 | rd_new[rd_count].data = buf; | 1335 | rd_new[rd_count].data = buf; |
1299 | rd_new[rd_count].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE; | 1336 | rd_new[rd_count].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE; |
1300 | rd_new[rd_count].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; | 1337 | rd_new[rd_count].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; |
1301 | rd_new[rd_count].expiration_time = ash->exp.rel_value_us; | 1338 | rd_new[rd_count].expiration_time = ash->exp.rel_value_us; |
1339 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting with label %s\n", label); | ||
1302 | ash->ns_qe = GNUNET_NAMESTORE_records_store (nsh, | 1340 | ash->ns_qe = GNUNET_NAMESTORE_records_store (nsh, |
1303 | &ash->identity, | 1341 | &ash->identity, |
1304 | label, | 1342 | label, |
@@ -1339,7 +1377,8 @@ reference_store_task (void *cls) | |||
1339 | 1377 | ||
1340 | label = GNUNET_STRINGS_data_to_string_alloc (&ash->reference->id, | 1378 | label = GNUNET_STRINGS_data_to_string_alloc (&ash->reference->id, |
1341 | sizeof(uint64_t)); | 1379 | sizeof(uint64_t)); |
1342 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting with label %s\n", label); | 1380 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
1381 | "Looking up existing data under label %s\n", label); | ||
1343 | // Test for the content of the existing ID | 1382 | // Test for the content of the existing ID |
1344 | 1383 | ||
1345 | ash->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh, | 1384 | ash->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh, |
@@ -1452,9 +1491,7 @@ ticket_iter (void *cls, | |||
1452 | int has_changed = GNUNET_NO; | 1491 | int has_changed = GNUNET_NO; |
1453 | for (int i = 0; i < rd_count; i++) | 1492 | for (int i = 0; i < rd_count; i++) |
1454 | { | 1493 | { |
1455 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type) && | 1494 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type) |
1456 | (GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE_REF != rd[i].record_type) && | ||
1457 | (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF != rd[i].record_type)) | ||
1458 | continue; | 1495 | continue; |
1459 | if (&adh->claim != NULL) | 1496 | if (&adh->claim != NULL) |
1460 | if (0 != memcmp (rd[i].data, &adh->claim->id, sizeof(uint64_t))) | 1497 | if (0 != memcmp (rd[i].data, &adh->claim->id, sizeof(uint64_t))) |
@@ -1559,10 +1596,10 @@ update_tickets (void *cls) | |||
1559 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF == rd[i].record_type) | 1596 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF == rd[i].record_type) |
1560 | && (0 == memcmp (rd[i].data, &adh->claim->id, sizeof(uint64_t)))) | 1597 | && (0 == memcmp (rd[i].data, &adh->claim->id, sizeof(uint64_t)))) |
1561 | continue; | 1598 | continue; |
1562 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE_REF == rd[i].record_type) | 1599 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF == rd[i].record_type) |
1563 | && (0 == memcmp (rd[i].data, &adh->attest->id, sizeof(uint64_t)))) | 1600 | && (0 == memcmp (rd[i].data, &adh->attest->id, sizeof(uint64_t)))) |
1564 | continue; | 1601 | continue; |
1565 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF == rd[i].record_type) | 1602 | if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF == rd[i].record_type) |
1566 | && (0 == memcmp (rd[i].data, &adh->reference->id, sizeof(uint64_t)))) | 1603 | && (0 == memcmp (rd[i].data, &adh->reference->id, sizeof(uint64_t)))) |
1567 | continue; | 1604 | continue; |
1568 | rd_new[j] = rd[i]; | 1605 | rd_new[j] = rd[i]; |
@@ -1879,7 +1916,7 @@ ref_del_cb (void *cls, | |||
1879 | { | 1916 | { |
1880 | data_tmp = GNUNET_malloc (rd[i].data_size); | 1917 | data_tmp = GNUNET_malloc (rd[i].data_size); |
1881 | GNUNET_memcpy (data_tmp, rd[i].data, rd[i].data_size); | 1918 | GNUNET_memcpy (data_tmp, rd[i].data, rd[i].data_size); |
1882 | attr_len = htons (rd->data_size); | 1919 | attr_len = htons (rd[i].data_size); |
1883 | ref = GNUNET_RECLAIM_ATTESTATION_REF_deserialize (data_tmp, attr_len); | 1920 | ref = GNUNET_RECLAIM_ATTESTATION_REF_deserialize (data_tmp, attr_len); |
1884 | if (NULL == ref ) | 1921 | if (NULL == ref ) |
1885 | { | 1922 | { |
@@ -2045,10 +2082,18 @@ attr_iter_cb (void *cls, | |||
2045 | } | 2082 | } |
2046 | if (rd_count > 1) | 2083 | if (rd_count > 1) |
2047 | { | 2084 | { |
2048 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR != rd[0].record_type) | 2085 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF == rd[0].record_type) |
2086 | { | ||
2087 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2088 | "Found Ticket. Ignoring.\n"); | ||
2089 | GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); | ||
2090 | return; | ||
2091 | } | ||
2092 | else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR != rd[0].record_type) | ||
2049 | { | 2093 | { |
2050 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 2094 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
2051 | "Non-Attestation record with multiple entries found\n"); | 2095 | "Non-Attestation record with multiple entries found: %u\n", |
2096 | rd[0].record_type); | ||
2052 | GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); | 2097 | GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); |
2053 | return; | 2098 | return; |
2054 | } | 2099 | } |
diff --git a/src/reclaim/gnunet-service-reclaim_tickets.c b/src/reclaim/gnunet-service-reclaim_tickets.c index 4d1a26333..b022225b8 100644 --- a/src/reclaim/gnunet-service-reclaim_tickets.c +++ b/src/reclaim/gnunet-service-reclaim_tickets.c | |||
@@ -667,8 +667,7 @@ rvk_move_attr_cb (void *cls, | |||
667 | const struct GNUNET_GNSRECORD_Data *rd) | 667 | const struct GNUNET_GNSRECORD_Data *rd) |
668 | { | 668 | { |
669 | struct RECLAIM_TICKETS_RevokeHandle *rvk = cls; | 669 | struct RECLAIM_TICKETS_RevokeHandle *rvk = cls; |
670 | struct GNUNET_RECLAIM_ATTRIBUTE_Claim *claim; | 670 | struct GNUNET_GNSRECORD_Data new_rd[rd_count]; |
671 | struct GNUNET_GNSRECORD_Data new_rd; | ||
672 | struct RevokedAttributeEntry *le; | 671 | struct RevokedAttributeEntry *le; |
673 | char *new_label; | 672 | char *new_label; |
674 | char *attr_data; | 673 | char *attr_data; |
@@ -677,7 +676,7 @@ rvk_move_attr_cb (void *cls, | |||
677 | if (0 == rd_count) | 676 | if (0 == rd_count) |
678 | { | 677 | { |
679 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | 678 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, |
680 | "The attribute %s no longer exists!\n", | 679 | "The claim %s no longer exists!\n", |
681 | label); | 680 | label); |
682 | le = rvk->move_attr; | 681 | le = rvk->move_attr; |
683 | rvk->move_attr = le->next; | 682 | rvk->move_attr = le->next; |
@@ -686,32 +685,82 @@ rvk_move_attr_cb (void *cls, | |||
686 | GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk); | 685 | GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk); |
687 | return; | 686 | return; |
688 | } | 687 | } |
689 | /** find a new place for this attribute **/ | 688 | rvk->move_attr->new_id =GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX); |
690 | rvk->move_attr->new_id = | 689 | new_label=NULL; |
691 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX); | 690 | attr_data=NULL; |
692 | new_rd = *rd; | 691 | //new_rd = *rd; |
693 | claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size); | 692 | for (int i = 0; i < rd_count; i++) |
694 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 693 | { |
695 | "Attribute to update: Name=%s, ID=%" PRIu64 "\n", | 694 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR == rd[i].record_type) |
696 | claim->name, | 695 | { |
697 | claim->id); | 696 | /** find a new place for this attribute **/ |
698 | claim->id = rvk->move_attr->new_id; | 697 | struct GNUNET_RECLAIM_ATTRIBUTE_Claim *claim; |
699 | new_rd.data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize_get_size (claim); | 698 | claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd[i].data, rd[i].data_size); |
700 | attr_data = GNUNET_malloc (rd->data_size); | 699 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
701 | new_rd.data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize (claim, attr_data); | 700 | "Attribute to update: Name=%s, ID=%" PRIu64 "\n", |
702 | new_rd.data = attr_data; | 701 | claim->name, |
703 | new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id, | 702 | claim->id); |
704 | sizeof(uint64_t)); | 703 | claim->id = rvk->move_attr->new_id; |
705 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute %s\n", new_label); | 704 | new_rd[i].data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize_get_size (claim); |
705 | attr_data = GNUNET_malloc (rd[i].data_size); | ||
706 | new_rd[i].data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize (claim, attr_data); | ||
707 | new_rd[i].data = attr_data; | ||
708 | new_rd[i].record_type = rd[i].record_type; | ||
709 | new_rd[i].flags = rd[i].flags; | ||
710 | new_rd[i].expiration_time = rd[i].expiration_time; | ||
711 | new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id, | ||
712 | sizeof(uint64_t)); | ||
713 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute %s\n", new_label); | ||
714 | GNUNET_free (claim); | ||
715 | } else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR == rd[i].record_type) | ||
716 | { | ||
717 | struct GNUNET_RECLAIM_ATTESTATION_Claim *attest; | ||
718 | attest=GNUNET_RECLAIM_ATTESTATION_deserialize(rd[i].data, rd[i].data_size); | ||
719 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
720 | "Attestation to update: Name=%s, ID=%" PRIu64 "\n", | ||
721 | attest->name, | ||
722 | attest->id); | ||
723 | attest->id = rvk->move_attr->new_id; | ||
724 | new_rd[i].data_size = GNUNET_RECLAIM_ATTESTATION_serialize_get_size (attest); | ||
725 | attr_data = GNUNET_malloc (rd[i].data_size); | ||
726 | new_rd[i].data_size = GNUNET_RECLAIM_ATTESTATION_serialize (attest, attr_data); | ||
727 | new_rd[i].data = attr_data; | ||
728 | new_rd[i].record_type = rd[i].record_type; | ||
729 | new_rd[i].flags = rd[i].flags; | ||
730 | new_rd[i].expiration_time = rd[i].expiration_time; | ||
731 | new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id, sizeof(uint64_t)); | ||
732 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attestation %s\n", new_label); | ||
733 | GNUNET_free (attest); | ||
734 | } else if (GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE == rd[i].record_type) | ||
735 | { | ||
736 | struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference; | ||
737 | reference=GNUNET_RECLAIM_ATTESTATION_REF_deserialize(rd[i].data, rd[i].data_size); | ||
738 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
739 | "Reference to update: Name=%s, ID=%" PRIu64 "\n", | ||
740 | reference->name, | ||
741 | reference->id); | ||
742 | reference->id = rvk->move_attr->new_id; | ||
743 | reference->id_attest = rvk->move_attr->new_id; | ||
744 | new_rd[i].data_size = GNUNET_RECLAIM_ATTESTATION_REF_serialize_get_size (reference); | ||
745 | attr_data = GNUNET_malloc (rd[i].data_size); | ||
746 | new_rd[i].data_size = GNUNET_RECLAIM_ATTESTATION_REF_serialize (reference, attr_data); | ||
747 | new_rd[i].data = attr_data; | ||
748 | new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id, sizeof(uint64_t)); | ||
749 | new_rd[i].record_type = rd[i].record_type; | ||
750 | new_rd[i].flags = rd[i].flags; | ||
751 | new_rd[i].expiration_time = rd[i].expiration_time; | ||
752 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding reference %s\n", new_label); | ||
753 | GNUNET_free (reference); | ||
754 | } | ||
755 | } | ||
706 | rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, | 756 | rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, |
707 | &rvk->identity, | 757 | &rvk->identity, |
708 | new_label, | 758 | new_label, |
709 | 1, | 759 | rd_count, |
710 | &new_rd, | 760 | new_rd, |
711 | &move_attr_finished, | 761 | &move_attr_finished, |
712 | rvk); | 762 | rvk); |
713 | GNUNET_free (new_label); | 763 | GNUNET_free (new_label); |
714 | GNUNET_free (claim); | ||
715 | GNUNET_free (attr_data); | 764 | GNUNET_free (attr_data); |
716 | } | 765 | } |
717 | 766 | ||
@@ -745,7 +794,7 @@ move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rvk) | |||
745 | } | 794 | } |
746 | label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id, | 795 | label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id, |
747 | sizeof(uint64_t)); | 796 | sizeof(uint64_t)); |
748 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving attribute %s\n", label); | 797 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving claim %s\n", label); |
749 | 798 | ||
750 | rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh, | 799 | rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh, |
751 | &rvk->identity, | 800 | &rvk->identity, |
@@ -982,21 +1031,70 @@ process_parallel_lookup_result (void *cls, | |||
982 | 1031 | ||
983 | 1032 | ||
984 | GNUNET_free (parallel_lookup); | 1033 | GNUNET_free (parallel_lookup); |
985 | if (1 != rd_count) | 1034 | if (0 == rd_count) |
986 | GNUNET_break (0); // FIXME: We should never find this. | 1035 | GNUNET_break (0); |
987 | if (rd->record_type == GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR) | 1036 | // REMARK: It is possible now to find rd_count > 1 |
1037 | for (int i = 0; i < rd_count; i++) | ||
988 | { | 1038 | { |
989 | attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); | 1039 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR == rd[i].record_type) |
990 | attr_le->claim = | 1040 | { |
991 | GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size); | 1041 | attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); |
992 | GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, | 1042 | attr_le->claim = |
993 | cth->attrs->list_tail, | 1043 | GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd[i].data, rd[i].data_size); |
994 | attr_le); | 1044 | GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, |
995 | } | 1045 | cth->attrs->list_tail, |
1046 | attr_le); | ||
1047 | attr_le->reference = NULL; | ||
1048 | attr_le->attest = NULL; | ||
1049 | } | ||
1050 | else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR == rd[i].record_type) | ||
1051 | { | ||
1052 | /**Ignore all plain attestations | ||
1053 | *attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); | ||
1054 | *attr_le->attest = | ||
1055 | * GNUNET_RECLAIM_ATTESTATION_deserialize (rd[i].data, rd[i].data_size); | ||
1056 | *GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, | ||
1057 | * cth->attrs->list_tail, | ||
1058 | * attr_le); | ||
1059 | */ | ||
1060 | continue; | ||
1061 | } | ||
1062 | else if (GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE == rd[i].record_type) | ||
1063 | { | ||
1064 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *attr_le2; | ||
1065 | attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); | ||
1066 | attr_le2 = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); | ||
1067 | if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR == rd[0].record_type) | ||
1068 | { | ||
1069 | attr_le->attest = GNUNET_RECLAIM_ATTESTATION_deserialize (rd[0].data, | ||
1070 | rd[0]. | ||
1071 | data_size); | ||
1072 | attr_le2->reference = | ||
1073 | GNUNET_RECLAIM_ATTESTATION_REF_deserialize (rd[i].data, | ||
1074 | rd[i].data_size); | ||
1075 | attr_le->claim = NULL; | ||
1076 | attr_le->reference = NULL; | ||
1077 | attr_le2->claim = NULL; | ||
1078 | attr_le2->attest = NULL; | ||
1079 | GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, | ||
1080 | cth->attrs->list_tail, | ||
1081 | attr_le); | ||
1082 | GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, | ||
1083 | cth->attrs->list_tail, | ||
1084 | attr_le2); | ||
1085 | } | ||
1086 | else | ||
1087 | { | ||
1088 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1089 | "Parallel Lookup of Reference without Attestation"); | ||
1090 | continue; | ||
1091 | } | ||
996 | 1092 | ||
1093 | |||
1094 | } | ||
1095 | } | ||
997 | if (NULL != cth->parallel_lookups_head) | 1096 | if (NULL != cth->parallel_lookups_head) |
998 | return; // Wait for more | 1097 | return; // Wait for more |
999 | |||
1000 | /* Else we are done */ | 1098 | /* Else we are done */ |
1001 | cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL); | 1099 | cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL); |
1002 | cleanup_cth (cth); | 1100 | cleanup_cth (cth); |
@@ -1076,7 +1174,7 @@ lookup_authz_cb (void *cls, | |||
1076 | GNUNET_GNS_lookup (gns, | 1174 | GNUNET_GNS_lookup (gns, |
1077 | lbl, | 1175 | lbl, |
1078 | &cth->ticket.identity, | 1176 | &cth->ticket.identity, |
1079 | GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR, | 1177 | GNUNET_GNSRECORD_TYPE_ANY, |
1080 | GNUNET_GNS_LO_DEFAULT, | 1178 | GNUNET_GNS_LO_DEFAULT, |
1081 | &process_parallel_lookup_result, | 1179 | &process_parallel_lookup_result, |
1082 | parallel_lookup); | 1180 | parallel_lookup); |
@@ -1223,6 +1321,7 @@ issue_ticket (struct TicketIssueHandle *ih) | |||
1223 | char *label; | 1321 | char *label; |
1224 | size_t list_len = 1; | 1322 | size_t list_len = 1; |
1225 | int i; | 1323 | int i; |
1324 | char *attest_string; | ||
1226 | 1325 | ||
1227 | for (le = ih->attrs->list_head; NULL != le; le = le->next) | 1326 | for (le = ih->attrs->list_head; NULL != le; le = le->next) |
1228 | list_len++; | 1327 | list_len++; |
@@ -1232,8 +1331,51 @@ issue_ticket (struct TicketIssueHandle *ih) | |||
1232 | i = 0; | 1331 | i = 0; |
1233 | for (le = ih->attrs->list_head; NULL != le; le = le->next) | 1332 | for (le = ih->attrs->list_head; NULL != le; le = le->next) |
1234 | { | 1333 | { |
1235 | attrs_record[i].data = &le->claim->id; | 1334 | if (NULL != le->claim) |
1236 | attrs_record[i].data_size = sizeof(le->claim->id); | 1335 | { |
1336 | attrs_record[i].data = &le->claim->id; | ||
1337 | attrs_record[i].data_size = sizeof(le->claim->id); | ||
1338 | } | ||
1339 | else if (NULL != le->attest) | ||
1340 | { | ||
1341 | // REMARK: Since we only store IDs, the references are irrelevant | ||
1342 | int j = 0; | ||
1343 | GNUNET_asprintf (&attest_string,"%d",le->attest->id); | ||
1344 | while (j<i) | ||
1345 | { | ||
1346 | if (0 == strcmp (attest_string,GNUNET_STRINGS_data_to_string_alloc ( | ||
1347 | attrs_record[j].data, attrs_record[j].data_size))) | ||
1348 | break; | ||
1349 | j++; | ||
1350 | } | ||
1351 | if (j < i) | ||
1352 | { | ||
1353 | list_len--; | ||
1354 | continue; | ||
1355 | } | ||
1356 | attrs_record[i].data = &le->attest->id; | ||
1357 | attrs_record[i].data_size = sizeof(le->attest->id); | ||
1358 | } | ||
1359 | else if (NULL != le->reference) | ||
1360 | { | ||
1361 | list_len--; | ||
1362 | continue; | ||
1363 | /* | ||
1364 | int j = 0; | ||
1365 | GNUNET_asprintf (&attest_string,"%d",le->attest->id); | ||
1366 | while (j<i) | ||
1367 | { | ||
1368 | if (strcmp(attest_string, GNUNET_STRINGS_data_to_string_alloc ( | ||
1369 | attrs_record[j].data, attrs_record[j].data_size))) | ||
1370 | break; | ||
1371 | j++; | ||
1372 | } | ||
1373 | if (j < i) | ||
1374 | continue; | ||
1375 | attrs_record[i].data = &le->reference->id; | ||
1376 | attrs_record[i].data_size = sizeof(le->reference->id); | ||
1377 | */ | ||
1378 | } | ||
1237 | /** | 1379 | /** |
1238 | * FIXME: Should this be the attribute expiration time or ticket | 1380 | * FIXME: Should this be the attribute expiration time or ticket |
1239 | * refresh interval? Probably min(attrs.expiration) | 1381 | * refresh interval? Probably min(attrs.expiration) |
@@ -1344,14 +1486,34 @@ filter_tickets_cb (void *cls, | |||
1344 | for (le = tih->attrs->list_head; NULL != le; le = le->next) | 1486 | for (le = tih->attrs->list_head; NULL != le; le = le->next) |
1345 | { | 1487 | { |
1346 | // cmp attr_ref id with requested attr id | 1488 | // cmp attr_ref id with requested attr id |
1347 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 1489 | if (NULL !=le->claim) |
1348 | " %" PRIu64 "\n %" PRIu64 "\n", | 1490 | { |
1349 | *((uint64_t *) rd[i].data), | 1491 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
1350 | le->claim->id); | 1492 | " %" PRIu64 "\n %" PRIu64 "\n", |
1351 | 1493 | *((uint64_t *) rd[i].data), | |
1494 | le->claim->id); | ||
1495 | if (0 == memcmp (rd[i].data, &le->claim->id, sizeof(uint64_t))) | ||
1496 | found_attrs_cnt++; | ||
1497 | } | ||
1498 | else if (NULL !=le->attest) | ||
1499 | { | ||
1500 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1501 | " %" PRIu64 "\n %" PRIu64 "\n", | ||
1502 | *((uint64_t *) rd[i].data), | ||
1503 | le->attest->id); | ||
1504 | if (0 == memcmp (rd[i].data, &le->attest->id, sizeof(uint64_t))) | ||
1505 | found_attrs_cnt++; | ||
1506 | } | ||
1507 | else if (NULL != le->reference) | ||
1508 | { | ||
1509 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1510 | " %" PRIu64 "\n %" PRIu64 "\n", | ||
1511 | *((uint64_t *) rd[i].data), | ||
1512 | le->reference->id); | ||
1513 | if (0 == memcmp (rd[i].data, &le->reference->id, sizeof(uint64_t))) | ||
1514 | found_attrs_cnt++; | ||
1515 | } | ||
1352 | 1516 | ||
1353 | if (0 == memcmp (rd[i].data, &le->claim->id, sizeof(uint64_t))) | ||
1354 | found_attrs_cnt++; | ||
1355 | } | 1517 | } |
1356 | } | 1518 | } |
1357 | 1519 | ||
diff --git a/src/reclaim/json_reclaim.c b/src/reclaim/json_reclaim.c index 552ca0e69..775ab58d6 100644 --- a/src/reclaim/json_reclaim.c +++ b/src/reclaim/json_reclaim.c | |||
@@ -48,6 +48,7 @@ parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) | |||
48 | const char *val_str = NULL; | 48 | const char *val_str = NULL; |
49 | const char *type_str = NULL; | 49 | const char *type_str = NULL; |
50 | const char *id_str = NULL; | 50 | const char *id_str = NULL; |
51 | const char *flag_str = NULL; | ||
51 | char *data; | 52 | char *data; |
52 | int unpack_state; | 53 | int unpack_state; |
53 | uint32_t type; | 54 | uint32_t type; |
@@ -63,7 +64,7 @@ parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) | |||
63 | } | 64 | } |
64 | // interpret single attribute | 65 | // interpret single attribute |
65 | unpack_state = json_unpack (root, | 66 | unpack_state = json_unpack (root, |
66 | "{s:s, s?s, s:s, s:s!}", | 67 | "{s:s, s?s, s:s, s:s, s?s!}", |
67 | "name", | 68 | "name", |
68 | &name_str, | 69 | &name_str, |
69 | "id", | 70 | "id", |
@@ -71,7 +72,9 @@ parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) | |||
71 | "type", | 72 | "type", |
72 | &type_str, | 73 | &type_str, |
73 | "value", | 74 | "value", |
74 | &val_str); | 75 | &val_str, |
76 | "flag", | ||
77 | &flag_str); | ||
75 | if ((0 != unpack_state) || (NULL == name_str) || (NULL == val_str) || | 78 | if ((0 != unpack_state) || (NULL == name_str) || (NULL == val_str) || |
76 | (NULL == type_str)) | 79 | (NULL == type_str)) |
77 | { | 80 | { |
diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c index 1c3d65f35..2ce462854 100644 --- a/src/reclaim/oidc_helper.c +++ b/src/reclaim/oidc_helper.c | |||
@@ -118,7 +118,7 @@ fix_base64 (char *str) | |||
118 | char * | 118 | char * |
119 | OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | 119 | OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, |
120 | const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, | 120 | const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, |
121 | const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, | 121 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, |
122 | const struct GNUNET_TIME_Relative *expiration_time, | 122 | const struct GNUNET_TIME_Relative *expiration_time, |
123 | const char *nonce, | 123 | const char *nonce, |
124 | const char *secret_key) | 124 | const char *secret_key) |
@@ -131,13 +131,22 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
131 | char *subject; | 131 | char *subject; |
132 | char *header; | 132 | char *header; |
133 | char *body_str; | 133 | char *body_str; |
134 | char *aggr_names_str; | ||
135 | char *aggr_sources_str; | ||
136 | char *aggr_sources_jwt_str; | ||
137 | char *source_name; | ||
134 | char *result; | 138 | char *result; |
135 | char *header_base64; | 139 | char *header_base64; |
136 | char *body_base64; | 140 | char *body_base64; |
137 | char *signature_target; | 141 | char *signature_target; |
138 | char *signature_base64; | 142 | char *signature_base64; |
139 | char *attr_val_str; | 143 | char *attr_val_str; |
144 | char *attest_val_str; | ||
140 | json_t *body; | 145 | json_t *body; |
146 | json_t *aggr_names; | ||
147 | json_t *aggr_sources; | ||
148 | json_t *aggr_sources_jwt; | ||
149 | uint64_t attest_arr[GNUNET_RECLAIM_ATTRIBUTE_list_count_attest (attrs)]; | ||
141 | 150 | ||
142 | // iat REQUIRED time now | 151 | // iat REQUIRED time now |
143 | time_now = GNUNET_TIME_absolute_get (); | 152 | time_now = GNUNET_TIME_absolute_get (); |
@@ -156,6 +165,8 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
156 | GNUNET_CRYPTO_EcdsaPublicKey)); | 165 | GNUNET_CRYPTO_EcdsaPublicKey)); |
157 | header = create_jwt_header (); | 166 | header = create_jwt_header (); |
158 | body = json_object (); | 167 | body = json_object (); |
168 | aggr_names = json_object (); | ||
169 | aggr_sources = json_object (); | ||
159 | 170 | ||
160 | // iss REQUIRED case sensitive server uri with https | 171 | // iss REQUIRED case sensitive server uri with https |
161 | // The issuer is the local reclaim instance (e.g. | 172 | // The issuer is the local reclaim instance (e.g. |
@@ -180,18 +191,111 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
180 | // nonce | 191 | // nonce |
181 | if (NULL != nonce) | 192 | if (NULL != nonce) |
182 | json_object_set_new (body, "nonce", json_string (nonce)); | 193 | json_object_set_new (body, "nonce", json_string (nonce)); |
183 | 194 | int i = 0; | |
195 | attest_val_str = NULL; | ||
196 | aggr_names_str = NULL; | ||
197 | aggr_sources_str = NULL; | ||
198 | aggr_sources_jwt_str = NULL; | ||
199 | source_name = NULL; | ||
184 | for (le = attrs->list_head; NULL != le; le = le->next) | 200 | for (le = attrs->list_head; NULL != le; le = le->next) |
185 | { | 201 | { |
186 | attr_val_str = | 202 | |
187 | GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type, | 203 | if (le->claim != NULL) |
188 | le->claim->data, | 204 | { |
189 | le->claim->data_size); | 205 | |
190 | json_object_set_new (body, le->claim->name, json_string (attr_val_str)); | 206 | attr_val_str = |
191 | GNUNET_free (attr_val_str); | 207 | GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type, |
208 | le->claim->data, | ||
209 | le->claim->data_size); | ||
210 | json_object_set_new (body, le->claim->name, json_string (attr_val_str)); | ||
211 | GNUNET_free (attr_val_str); | ||
212 | } | ||
213 | else if (NULL != le->reference) | ||
214 | { | ||
215 | // Check if attest is there | ||
216 | int j = 0; | ||
217 | while (j<i) | ||
218 | { | ||
219 | if (attest_arr[j] == le->reference->id_attest) | ||
220 | break; | ||
221 | j++; | ||
222 | } | ||
223 | if (j==i) | ||
224 | { | ||
225 | // Attest not yet existent. Append to the end of the list | ||
226 | GNUNET_CONTAINER_DLL_remove (attrs->list_head, attrs->list_tail, le); | ||
227 | GNUNET_CONTAINER_DLL_insert_tail (attrs->list_head, attrs->list_tail, | ||
228 | le); | ||
229 | continue; | ||
230 | } | ||
231 | else | ||
232 | { | ||
233 | // Attestation is existing, hence take the respective source str | ||
234 | GNUNET_asprintf (&source_name, | ||
235 | "src%d", | ||
236 | j); | ||
237 | json_object_set_new (aggr_names, le->reference->name, json_string ( | ||
238 | source_name)); | ||
239 | } | ||
240 | |||
241 | } | ||
242 | else if (NULL != le->attest) | ||
243 | { | ||
244 | // We assume that at max 99 different attestations | ||
245 | int j = 0; | ||
246 | while (j<i) | ||
247 | { | ||
248 | if (attest_arr[j] == le->attest->id) | ||
249 | break; | ||
250 | j++; | ||
251 | } | ||
252 | if (j==i) | ||
253 | { | ||
254 | // New Attestation | ||
255 | attest_arr[i] = le->attest->id; | ||
256 | GNUNET_asprintf (&source_name, | ||
257 | "src%d", | ||
258 | i); | ||
259 | aggr_sources_jwt = json_object (); | ||
260 | attest_val_str = GNUNET_RECLAIM_ATTESTATION_value_to_string ( | ||
261 | le->attest->type, le->attest->data, le->attest->data_size); | ||
262 | json_object_set_new (aggr_sources_jwt, "JWT",json_string ( | ||
263 | attest_val_str) ); | ||
264 | aggr_sources_jwt_str = json_dumps (aggr_sources_jwt, JSON_INDENT (0) | ||
265 | | JSON_COMPACT); | ||
266 | json_object_set_new (aggr_sources, source_name,json_string ( | ||
267 | aggr_sources_jwt_str)); | ||
268 | i++; | ||
269 | } | ||
270 | else | ||
271 | { | ||
272 | // Attestation already existent. Ignore | ||
273 | continue; | ||
274 | } | ||
275 | |||
276 | } | ||
277 | } | ||
278 | if (NULL != attest_val_str) | ||
279 | GNUNET_free (attest_val_str); | ||
280 | if (NULL != source_name) | ||
281 | GNUNET_free (source_name); | ||
282 | if (0!=i) | ||
283 | { | ||
284 | aggr_names_str = json_dumps (aggr_names, JSON_INDENT (0) | JSON_COMPACT); | ||
285 | aggr_sources_str = json_dumps (aggr_sources, JSON_INDENT (0) | ||
286 | | JSON_COMPACT); | ||
287 | json_object_set_new (body, "_claim_names", json_string (aggr_names_str)); | ||
288 | json_object_set_new (body, "_claim_sources", json_string ( | ||
289 | aggr_sources_str)); | ||
192 | } | 290 | } |
291 | |||
292 | json_decref (aggr_names); | ||
293 | json_decref (aggr_sources); | ||
294 | json_decref (aggr_sources_jwt); | ||
295 | |||
193 | body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); | 296 | body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); |
194 | json_decref (body); | 297 | json_decref (body); |
298 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str); | ||
195 | 299 | ||
196 | GNUNET_STRINGS_base64_encode (header, strlen (header), &header_base64); | 300 | GNUNET_STRINGS_base64_encode (header, strlen (header), &header_base64); |
197 | fix_base64 (header_base64); | 301 | fix_base64 (header_base64); |
@@ -226,6 +330,12 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
226 | GNUNET_free (signature_target); | 330 | GNUNET_free (signature_target); |
227 | GNUNET_free (header); | 331 | GNUNET_free (header); |
228 | GNUNET_free (body_str); | 332 | GNUNET_free (body_str); |
333 | if (NULL != aggr_sources_str) | ||
334 | GNUNET_free (aggr_sources_str); | ||
335 | if (NULL != aggr_names_str) | ||
336 | GNUNET_free (aggr_names_str); | ||
337 | if (NULL != aggr_sources_jwt_str) | ||
338 | GNUNET_free (aggr_sources_jwt_str); | ||
229 | GNUNET_free (signature_base64); | 339 | GNUNET_free (signature_base64); |
230 | GNUNET_free (body_base64); | 340 | GNUNET_free (body_base64); |
231 | GNUNET_free (header_base64); | 341 | GNUNET_free (header_base64); |
diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h index 1774618e8..a7072755b 100644 --- a/src/reclaim/oidc_helper.h +++ b/src/reclaim/oidc_helper.h | |||
@@ -51,7 +51,7 @@ | |||
51 | char* | 51 | char* |
52 | OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | 52 | OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, |
53 | const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, | 53 | const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, |
54 | const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, | 54 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, |
55 | const struct GNUNET_TIME_Relative *expiration_time, | 55 | const struct GNUNET_TIME_Relative *expiration_time, |
56 | const char *nonce, | 56 | const char *nonce, |
57 | const char *secret_key); | 57 | const char *secret_key); |
diff --git a/src/reclaim/plugin_gnsrecord_reclaim.c b/src/reclaim/plugin_gnsrecord_reclaim.c index 0f59082dc..f7145a272 100644 --- a/src/reclaim/plugin_gnsrecord_reclaim.c +++ b/src/reclaim/plugin_gnsrecord_reclaim.c | |||
@@ -122,8 +122,6 @@ static struct | |||
122 | { "RECLAIM_OIDC_REDIRECT", GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT }, | 122 | { "RECLAIM_OIDC_REDIRECT", GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT }, |
123 | { "RECLAIM_TICKET", GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET }, | 123 | { "RECLAIM_TICKET", GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET }, |
124 | { "RECLAIM_REFERENCE", GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE }, | 124 | { "RECLAIM_REFERENCE", GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE }, |
125 | { "RECLAIM_REFERENCE_REF", GNUNET_GNSRECORD_TYPE_RECLAIM_REFERENCE_REF }, | ||
126 | { "RECLAIM_ATTEST_REF", GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF }, | ||
127 | { NULL, UINT32_MAX } | 125 | { NULL, UINT32_MAX } |
128 | }; | 126 | }; |
129 | 127 | ||
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c index 2c4b75c3f..741094f21 100644 --- a/src/reclaim/plugin_rest_openid_connect.c +++ b/src/reclaim/plugin_rest_openid_connect.c | |||
@@ -120,6 +120,11 @@ | |||
120 | #define OIDC_NONCE_KEY "nonce" | 120 | #define OIDC_NONCE_KEY "nonce" |
121 | 121 | ||
122 | /** | 122 | /** |
123 | * OIDC claims key | ||
124 | */ | ||
125 | #define OIDC_CLAIMS_KEY "claims" | ||
126 | |||
127 | /** | ||
123 | * OIDC PKCE code challenge | 128 | * OIDC PKCE code challenge |
124 | */ | 129 | */ |
125 | #define OIDC_CODE_CHALLENGE_KEY "code_challenge" | 130 | #define OIDC_CODE_CHALLENGE_KEY "code_challenge" |
@@ -291,6 +296,11 @@ struct OIDC_Variables | |||
291 | char *nonce; | 296 | char *nonce; |
292 | 297 | ||
293 | /** | 298 | /** |
299 | * The OIDC claims | ||
300 | */ | ||
301 | char *claims; | ||
302 | |||
303 | /** | ||
294 | * The OIDC response type | 304 | * The OIDC response type |
295 | */ | 305 | */ |
296 | char *response_type; | 306 | char *response_type; |
@@ -560,7 +570,12 @@ cleanup_handle (struct RequestHandle *handle) | |||
560 | { | 570 | { |
561 | claim_tmp = claim_entry; | 571 | claim_tmp = claim_entry; |
562 | claim_entry = claim_entry->next; | 572 | claim_entry = claim_entry->next; |
563 | GNUNET_free (claim_tmp->claim); | 573 | if (NULL != claim_tmp->claim) |
574 | GNUNET_free (claim_tmp->claim); | ||
575 | if (NULL != claim_tmp->attest) | ||
576 | GNUNET_free (claim_tmp->attest); | ||
577 | if (NULL != claim_tmp->reference) | ||
578 | GNUNET_free (claim_tmp->reference); | ||
564 | GNUNET_free (claim_tmp); | 579 | GNUNET_free (claim_tmp); |
565 | } | 580 | } |
566 | GNUNET_free (handle->attr_list); | 581 | GNUNET_free (handle->attr_list); |
@@ -697,7 +712,7 @@ return_userinfo_response (void *cls) | |||
697 | struct MHD_Response *resp; | 712 | struct MHD_Response *resp; |
698 | 713 | ||
699 | result_str = json_dumps (handle->oidc->response, 0); | 714 | result_str = json_dumps (handle->oidc->response, 0); |
700 | 715 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"ID-Token: %s\n",result_str); | |
701 | resp = GNUNET_REST_create_response (result_str); | 716 | resp = GNUNET_REST_create_response (result_str); |
702 | handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); | 717 | handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); |
703 | GNUNET_free (result_str); | 718 | GNUNET_free (result_str); |
@@ -838,7 +853,7 @@ login_redirect (void *cls) | |||
838 | &login_base_url)) | 853 | &login_base_url)) |
839 | { | 854 | { |
840 | GNUNET_asprintf (&new_redirect, | 855 | GNUNET_asprintf (&new_redirect, |
841 | "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", | 856 | "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", |
842 | login_base_url, | 857 | login_base_url, |
843 | OIDC_RESPONSE_TYPE_KEY, | 858 | OIDC_RESPONSE_TYPE_KEY, |
844 | handle->oidc->response_type, | 859 | handle->oidc->response_type, |
@@ -854,7 +869,10 @@ login_redirect (void *cls) | |||
854 | (NULL != handle->oidc->code_challenge) ? | 869 | (NULL != handle->oidc->code_challenge) ? |
855 | handle->oidc->code_challenge : "", | 870 | handle->oidc->code_challenge : "", |
856 | OIDC_NONCE_KEY, | 871 | OIDC_NONCE_KEY, |
857 | (NULL != handle->oidc->nonce) ? handle->oidc->nonce : ""); | 872 | (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "", |
873 | OIDC_CLAIMS_KEY, | ||
874 | (NULL != handle->oidc->claims) ? handle->oidc->claims : | ||
875 | ""); | ||
858 | resp = GNUNET_REST_create_response (""); | 876 | resp = GNUNET_REST_create_response (""); |
859 | MHD_add_response_header (resp, "Location", new_redirect); | 877 | MHD_add_response_header (resp, "Location", new_redirect); |
860 | GNUNET_free (login_base_url); | 878 | GNUNET_free (login_base_url); |
@@ -993,7 +1011,7 @@ oidc_attr_collect (void *cls, | |||
993 | GNUNET_RECLAIM_get_attributes_next (handle->attr_it); | 1011 | GNUNET_RECLAIM_get_attributes_next (handle->attr_it); |
994 | return; | 1012 | return; |
995 | } | 1013 | } |
996 | if (NULL == attr) | 1014 | if (NULL != reference) |
997 | { | 1015 | { |
998 | if ((NULL == reference->name) || (NULL == reference->reference_value)) | 1016 | if ((NULL == reference->name) || (NULL == reference->reference_value)) |
999 | { | 1017 | { |
@@ -1013,35 +1031,31 @@ oidc_attr_collect (void *cls, | |||
1013 | return; | 1031 | return; |
1014 | } | 1032 | } |
1015 | GNUNET_free (scope_variables); | 1033 | GNUNET_free (scope_variables); |
1016 | // Store references as attributes as they only use the ID later | 1034 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le2; |
1017 | const char *type_str = NULL; | 1035 | le2 = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); |
1018 | char *data; | ||
1019 | size_t data_size; | ||
1020 | uint32_t type; | ||
1021 | le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); | 1036 | le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); |
1022 | type_str = "String"; | 1037 | le->claim = NULL; |
1023 | type = GNUNET_RECLAIM_ATTRIBUTE_typename_to_number (type_str); | 1038 | le->reference = NULL; |
1024 | if (GNUNET_SYSERR ==(GNUNET_RECLAIM_ATTRIBUTE_string_to_value (type, | 1039 | le->attest = GNUNET_RECLAIM_ATTESTATION_claim_new (attest->name, |
1025 | reference-> | 1040 | attest->type, |
1026 | reference_value, | 1041 | attest->data, |
1027 | (void **) & | 1042 | attest->data_size); |
1028 | data, | 1043 | le->attest->id = attest->id; |
1029 | &data_size))) | 1044 | le2->attest = NULL; |
1030 | { | 1045 | le2->claim = NULL; |
1031 | return; | 1046 | le2->reference = GNUNET_RECLAIM_ATTESTATION_reference_new (reference->name, |
1032 | } | 1047 | reference-> |
1033 | le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (reference->name, | 1048 | reference_value); |
1034 | type, | 1049 | le2->reference->id = reference->id; |
1035 | data, | 1050 | le2->reference->id_attest = reference->id_attest; |
1036 | data_size); | ||
1037 | le->claim->id = reference->id; | ||
1038 | le->claim->flag = 1; | ||
1039 | |||
1040 | GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head, | 1051 | GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head, |
1041 | handle->attr_list->list_tail, | 1052 | handle->attr_list->list_tail, |
1042 | le); | 1053 | le); |
1054 | GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head, | ||
1055 | handle->attr_list->list_tail, | ||
1056 | le2); | ||
1043 | } | 1057 | } |
1044 | else | 1058 | else if (NULL != attr) |
1045 | { | 1059 | { |
1046 | if ((NULL == attr->name) || (NULL == attr->data)) | 1060 | if ((NULL == attr->name) || (NULL == attr->data)) |
1047 | { | 1061 | { |
@@ -1063,8 +1077,9 @@ oidc_attr_collect (void *cls, | |||
1063 | return; | 1077 | return; |
1064 | } | 1078 | } |
1065 | GNUNET_free (scope_variables); | 1079 | GNUNET_free (scope_variables); |
1066 | |||
1067 | le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); | 1080 | le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); |
1081 | le->reference = NULL; | ||
1082 | le->attest = NULL; | ||
1068 | le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, | 1083 | le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, |
1069 | attr->type, | 1084 | attr->type, |
1070 | attr->data, | 1085 | attr->data, |
@@ -1362,6 +1377,9 @@ build_authz_response (void *cls) | |||
1362 | // OPTIONAL value: nonce | 1377 | // OPTIONAL value: nonce |
1363 | handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY); | 1378 | handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY); |
1364 | 1379 | ||
1380 | // OPTIONAL value: claims | ||
1381 | handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY); | ||
1382 | |||
1365 | // TODO check other values if needed | 1383 | // TODO check other values if needed |
1366 | number_of_ignored_parameter = | 1384 | number_of_ignored_parameter = |
1367 | sizeof(OIDC_ignored_parameter_array) / sizeof(char *); | 1385 | sizeof(OIDC_ignored_parameter_array) / sizeof(char *); |
@@ -1918,8 +1936,8 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
1918 | 1936 | ||
1919 | 1937 | ||
1920 | /** | 1938 | /** |
1921 | * Collects claims and stores them in handle | 1939 | * Collects claims and stores them in handle |
1922 | */ | 1940 | */ |
1923 | static void | 1941 | static void |
1924 | consume_ticket (void *cls, | 1942 | consume_ticket (void *cls, |
1925 | const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, | 1943 | const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, |
@@ -1928,20 +1946,87 @@ consume_ticket (void *cls, | |||
1928 | const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference) | 1946 | const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference) |
1929 | { | 1947 | { |
1930 | struct RequestHandle *handle = cls; | 1948 | struct RequestHandle *handle = cls; |
1931 | char *tmp_value; | ||
1932 | json_t *value; | ||
1933 | |||
1934 | if (NULL == identity) | 1949 | if (NULL == identity) |
1935 | { | 1950 | { |
1936 | GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle); | 1951 | GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle); |
1937 | return; | 1952 | return; |
1938 | } | 1953 | } |
1939 | tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, | 1954 | if (NULL != attr) |
1940 | attr->data, | 1955 | { |
1941 | attr->data_size); | 1956 | char *tmp_value; |
1942 | value = json_string (tmp_value); | 1957 | json_t *value; |
1943 | json_object_set_new (handle->oidc->response, attr->name, value); | 1958 | tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, |
1944 | GNUNET_free (tmp_value); | 1959 | attr->data, |
1960 | attr->data_size); | ||
1961 | value = json_string (tmp_value); | ||
1962 | json_object_set_new (handle->oidc->response, attr->name, value); | ||
1963 | GNUNET_free (tmp_value); | ||
1964 | } | ||
1965 | else if ((NULL != attest) && (NULL != reference)) | ||
1966 | { | ||
1967 | json_t *claim_sources; | ||
1968 | json_t *claim_sources_jwt; | ||
1969 | json_t *claim_names; | ||
1970 | char *attest_val_str; | ||
1971 | claim_sources=json_object_get(handle->oidc->response,"_claim_sources"); | ||
1972 | claim_names=json_object_get(handle->oidc->response,"_claim_names"); | ||
1973 | attest_val_str = GNUNET_RECLAIM_ATTESTATION_value_to_string (attest->type, | ||
1974 | attest->data, | ||
1975 | attest-> | ||
1976 | data_size); | ||
1977 | if ((NULL == claim_sources) && (NULL == claim_names) ) | ||
1978 | { | ||
1979 | claim_sources = json_object (); | ||
1980 | claim_names = json_object (); | ||
1981 | } | ||
1982 | char *source_name; | ||
1983 | int i = 0; | ||
1984 | GNUNET_asprintf (&source_name,"src%d",i); | ||
1985 | while (NULL != (claim_sources_jwt = json_object_get (claim_sources, | ||
1986 | source_name))) | ||
1987 | { | ||
1988 | if (0 == strcmp (json_string_value (json_object_get (claim_sources_jwt, | ||
1989 | "JWT")), | ||
1990 | attest_val_str)) | ||
1991 | { | ||
1992 | // Adapt only the claim names | ||
1993 | json_object_set_new (claim_names, reference->name, json_string ( | ||
1994 | source_name)); | ||
1995 | json_object_set (handle->oidc->response, "_claim_names",claim_names); | ||
1996 | handle->oidc->response = json_deep_copy(handle->oidc->response); | ||
1997 | break; | ||
1998 | } | ||
1999 | i++; | ||
2000 | GNUNET_asprintf (&source_name,"src%d",i); | ||
2001 | } | ||
2002 | |||
2003 | // Create new one | ||
2004 | if (NULL == claim_sources_jwt) | ||
2005 | { | ||
2006 | claim_sources_jwt = json_object (); | ||
2007 | // Set the JWT for names | ||
2008 | json_object_set_new (claim_names, reference->name, json_string ( | ||
2009 | source_name)); | ||
2010 | // Set the JWT for the inner source | ||
2011 | json_object_set_new (claim_sources_jwt, "JWT", json_string ( | ||
2012 | attest_val_str)); | ||
2013 | // Set the JWT for the source | ||
2014 | json_object_set_new (claim_sources, source_name,claim_sources_jwt); | ||
2015 | // Set as claims | ||
2016 | json_object_set (handle->oidc->response, "_claim_names", claim_names); | ||
2017 | json_object_set (handle->oidc->response, "_claim_sources",claim_sources); | ||
2018 | handle->oidc->response = json_deep_copy(handle->oidc->response); | ||
2019 | } | ||
2020 | |||
2021 | json_decref (claim_sources); | ||
2022 | json_decref (claim_names); | ||
2023 | json_decref (claim_sources_jwt); | ||
2024 | GNUNET_free (attest_val_str); | ||
2025 | } | ||
2026 | else | ||
2027 | { | ||
2028 | // REMARK: We should not find any claim, one of attest/ref is NULL | ||
2029 | } | ||
1945 | } | 2030 | } |
1946 | 2031 | ||
1947 | 2032 | ||
diff --git a/src/reclaim/plugin_rest_reclaim.c b/src/reclaim/plugin_rest_reclaim.c index 16286444a..70defae3d 100644 --- a/src/reclaim/plugin_rest_reclaim.c +++ b/src/reclaim/plugin_rest_reclaim.c | |||
@@ -276,6 +276,8 @@ cleanup_handle (struct RequestHandle *handle) | |||
276 | claim_tmp = claim_entry; | 276 | claim_tmp = claim_entry; |
277 | claim_entry = claim_entry->next; | 277 | claim_entry = claim_entry->next; |
278 | GNUNET_free (claim_tmp->claim); | 278 | GNUNET_free (claim_tmp->claim); |
279 | GNUNET_free (claim_tmp->attest); | ||
280 | GNUNET_free (claim_tmp->reference); | ||
279 | GNUNET_free (claim_tmp); | 281 | GNUNET_free (claim_tmp); |
280 | } | 282 | } |
281 | GNUNET_free (handle->attr_list); | 283 | GNUNET_free (handle->attr_list); |
@@ -1286,6 +1288,7 @@ attr_collect (void *cls, | |||
1286 | return; | 1288 | return; |
1287 | } | 1289 | } |
1288 | char *tmp_value; | 1290 | char *tmp_value; |
1291 | char *flag_str; | ||
1289 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", attr->name); | 1292 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", attr->name); |
1290 | 1293 | ||
1291 | tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, | 1294 | tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, |
@@ -1295,6 +1298,8 @@ attr_collect (void *cls, | |||
1295 | attr_obj = json_object (); | 1298 | attr_obj = json_object (); |
1296 | json_object_set_new (attr_obj, "value", json_string (tmp_value)); | 1299 | json_object_set_new (attr_obj, "value", json_string (tmp_value)); |
1297 | json_object_set_new (attr_obj, "name", json_string (attr->name)); | 1300 | json_object_set_new (attr_obj, "name", json_string (attr->name)); |
1301 | GNUNET_asprintf (&flag_str,"%d",attr->flag); | ||
1302 | json_object_set_new (attr_obj, "flag", json_string (flag_str)); | ||
1298 | type = GNUNET_RECLAIM_ATTRIBUTE_number_to_typename (attr->type); | 1303 | type = GNUNET_RECLAIM_ATTRIBUTE_number_to_typename (attr->type); |
1299 | json_object_set_new (attr_obj, "type", json_string (type)); | 1304 | json_object_set_new (attr_obj, "type", json_string (type)); |
1300 | id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(uint64_t)); | 1305 | id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(uint64_t)); |
diff --git a/src/reclaim/reclaim_api.c b/src/reclaim/reclaim_api.c index 75ef22c8c..847abb58a 100644 --- a/src/reclaim/reclaim_api.c +++ b/src/reclaim/reclaim_api.c | |||
@@ -486,7 +486,7 @@ handle_consume_ticket_result (void *cls, | |||
486 | uint32_t r_id = ntohl (msg->id); | 486 | uint32_t r_id = ntohl (msg->id); |
487 | 487 | ||
488 | attrs_len = ntohs (msg->attrs_len); | 488 | attrs_len = ntohs (msg->attrs_len); |
489 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing attribute result.\n"); | 489 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing ticket result.\n"); |
490 | 490 | ||
491 | 491 | ||
492 | for (op = h->op_head; NULL != op; op = op->next) | 492 | for (op = h->op_head; NULL != op; op = op->next) |
@@ -498,6 +498,7 @@ handle_consume_ticket_result (void *cls, | |||
498 | { | 498 | { |
499 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs; | 499 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs; |
500 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; | 500 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; |
501 | struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le2; | ||
501 | attrs = | 502 | attrs = |
502 | GNUNET_RECLAIM_ATTRIBUTE_list_deserialize ((char *) &msg[1], attrs_len); | 503 | GNUNET_RECLAIM_ATTRIBUTE_list_deserialize ((char *) &msg[1], attrs_len); |
503 | if (NULL != op->ar_cb) | 504 | if (NULL != op->ar_cb) |
@@ -509,7 +510,20 @@ handle_consume_ticket_result (void *cls, | |||
509 | else | 510 | else |
510 | { | 511 | { |
511 | for (le = attrs->list_head; NULL != le; le = le->next) | 512 | for (le = attrs->list_head; NULL != le; le = le->next) |
512 | op->ar_cb (op->cls, &msg->identity, le->claim, NULL, NULL); | 513 | { |
514 | if (le->reference != NULL && le->attest == NULL) | ||
515 | { | ||
516 | for (le2 = attrs->list_head; NULL != le2; le2 = le2->next) | ||
517 | { | ||
518 | if (le2->attest != NULL && le2->attest->id == le->reference->id_attest) | ||
519 | { | ||
520 | op->ar_cb (op->cls, &msg->identity, le->claim, le2->attest, le->reference); | ||
521 | break; | ||
522 | } | ||
523 | |||
524 | } | ||
525 | } | ||
526 | } | ||
513 | GNUNET_RECLAIM_ATTRIBUTE_list_destroy (attrs); | 527 | GNUNET_RECLAIM_ATTRIBUTE_list_destroy (attrs); |
514 | attrs = NULL; | 528 | attrs = NULL; |
515 | } | 529 | } |