1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006
|
/**************************************************************************
*** COPYRIGHT (c) 2002 by TransNexus, Inc. ***
*** ***
*** This software is property of TransNexus, Inc. ***
*** This software is freely available under license from TransNexus. ***
*** The license terms and conditions for free use of this software by ***
*** third parties are defined in the OSP Toolkit Software License ***
*** Agreement (LICENSE.txt). Any use of this software by third ***
*** parties, which does not comply with the terms and conditions of the ***
*** OSP Toolkit Software License Agreement is prohibited without ***
*** the prior, express, written consent of TransNexus, Inc. ***
*** ***
*** Thank you for using the OSP ToolKit(TM). Please report any bugs, ***
*** suggestions or feedback to support@transnexus.com ***
*** ***
**************************************************************************/
#include "osp/ospmsginfo.h"
#include "osp/osp.h"
#include "osp/ospssl.h"
#include "osp/osputils.h"
#include "osp/ospcomm.h"
#include "osp/ospsecurity.h"
#include "osp/ospb64.h"
#include "osptnep.h"
#include "osptnepenroll.h"
/*
* Perform all of the operations for starting up a communications manager,
* retrieving a CA certificate, formulating requests, and sending requests.
*
* Input: a pointer to the enrollment and communications parameters, as
* well as a pointer to the certificate that is being requested and
* the status of the request.
*
* Output: The success for creating an enrollment request, starting up the
* communications manager, and parsing the response. Note that an
* error code will not be generated if the response fails; an error
* could be due to the enrollment server, so any failed requests
* will be recorded in the ospvEnrollStatusOut parameter.
*/
int OSPPEnroll(OSPTENROLLPARAMS *ospvEnrollParamsIn,
OSPTCOMMPARAMS *ospvCommParamsIn,
unsigned char **ospvLocalCertOut,
unsigned *ospvLocalCertLenOut,
unsigned *ospvEnrollStatusOut)
{
int retVal = OSPC_ERR_NO_ERROR;
/*
* This is for any warnings that may pop up; it should only be
* used for errors that occur while cleaning up or other non-fatal errors.
*/
int warnVal = OSPC_ERR_NO_ERROR;
/*
* This is the structure whose request is transmitted to the enrollment
* server and whose Response field is scanned for the response.
*/
OSPT_MSG_INFO *enrollMsg = OSPC_OSNULL;
/* This is the communications manager that will handle all of the requests to the enrollment server: */
OSPTCOMM *commMgr = OSPC_OSNULL;
/* This is used for comparison against the public key that is returned. */
OSPTASN1OBJECT requestPublicKey;
OSPM_DBGENTER(("ENTER: OSPPEnroll\n"));
/*
* IF ( the parameters passed in are ok ) then
* o proceed with initialization and enrollment...
*/
if ((ospvEnrollParamsIn == OSPC_OSNULL) || (ospvLocalCertOut == OSPC_OSNULL) || (ospvLocalCertLenOut == OSPC_OSNULL) ||
(ospvEnrollStatusOut == OSPC_OSNULL))
{
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The parameters passed in for enrollment were invalid. )\n");
}
/*
* If ( the input parameters were ok ) then
* o decode the base64-encoded certificate in the enrollment parameters
* o complain if we can't do it
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPBase64DecodeCACert(ospvEnrollParamsIn);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to base64-decode the CA certificate passed in.\n");
}
}
/*
* Create the structure that will be used for transmitting the
* enrollment request:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPMsgInfoNew(&enrollMsg);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to initialize MessageInfo for enrollment request.\n");
}
}
/*
* If ( the initialization was successful ) then:
* - initialize the response message to be empty
* - retrieve the subject public key info from the certificate
* request ( so that we can compare it to what we receive. )
*/
if (retVal == OSPC_ERR_NO_ERROR) {
enrollMsg->ResponseMsg = OSPC_OSNULL;
retVal = OSPPGetPublicKeyInfoFromCertReq(ospvEnrollParamsIn->CertReq, &requestPublicKey);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Cannot find public key info in PKCS#10 cert request.\n");
}
}
/* Initialize the request for the enrollMsg using the input parameters: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPConstructEnrollmentRequest(ospvEnrollParamsIn, enrollMsg);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to construct enrollment request for transmission.\n");
}
}
/* If ( we could initialize the MsgInfo's Request ) then Initialize the comm mgr. */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPInitSecureCommMgr(ospvEnrollParamsIn, ospvCommParamsIn, &commMgr);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to initialize secure enrollment communications.\n");
}
}
/* If ( there's a comm mgr that works ) then try enrolling the device. */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPEnrollDevice(commMgr, enrollMsg, &requestPublicKey, ospvEnrollStatusOut, ospvLocalCertOut, ospvLocalCertLenOut);
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("Message successfully received from enrollment server.\n"));
if (*ospvEnrollStatusOut == OSPC_ENROLL_STATUS_OK) {
OSPM_DBGMISC(("Certificate request successful.\n"));
} else if (*ospvEnrollStatusOut == OSPC_ENROLL_STATUS_PENDING) {
OSPM_DBGMISC(("Certificate requeest pending approval.\n"));
} else {
OSPM_DBGMISC(("Request rejected: <%d>\n", *ospvEnrollStatusOut));
retVal = *ospvEnrollStatusOut;
}
} else {
OSPM_DBGERRORLOG(retVal, "Unable to retrieve certificate from enrollment server.\n");
}
}
/* Cleanup the comm mgr: */
if (commMgr != OSPC_OSNULL) {
warnVal = OSPPEnrollCleanupCommMgr(&commMgr);
if (warnVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("Warning: error while cleaning up comm manager: <%d>\n", warnVal));
}
}
/* Cleanup the message info: */
if (enrollMsg != OSPC_OSNULL) {
OSPPMsgInfoDelete(&enrollMsg);
}
/* Cleanup the public key info's element info: */
if (requestPublicKey.ElementInfo != OSPC_OSNULL) {
OSPPASN1ElementDelete(&(requestPublicKey.ElementInfo), 0);
}
/*
* Now cleanup the public key info's parse results ( which links the
* whole ASN1 structure together ):
*/
if (requestPublicKey.ParseResults != OSPC_OSNULL) {
PTPResultsDelete(&(requestPublicKey.ParseResults));
}
OSPM_DBGEXIT(("EXIT: OSPPEnroll\n"));
return retVal;
}
/*
* Given the enrollment parameters, base64-decode the CACertB64 parameter
* of the enrollment parmaeters and place the binary CA certificate in the
* CACert field of the enrollment parameters instead.
*
* Input: pointer to the enrollment parameters, which should contain a pointer
* the base64-encoded CA certificate and its length, as well as pointers
* to the binary CA certificate and its length ( which will initially
* be NULL and 0. )
*
* Output: OSPC_ERR_NO_ERROR is returned if there are no problems, or
* a non-OSPC_ERR_NO_ERROR value is returned. If everything is
* successful, then the CACertB64 field should be populated as well.
*/
int OSPPBase64DecodeCACert(OSPTENROLLPARAMS *ospvEnrollParamsIn)
{
int retVal = OSPC_ERR_NO_ERROR;
/* The base64-decoded certificate, if it can be determined: */
unsigned char *caCert = OSPC_OSNULL;
/* The length of the binary ( base64-decoded ) certificate: */
unsigned caCertLen = 0;
/*
* Check the pointer to the enrollment parameters and the base64-encoded
* certificate. If they're not quite right, then set an error code and
* complain.
*/
if ((ospvEnrollParamsIn == OSPC_OSNULL) || (ospvEnrollParamsIn->CACertB64 == OSPC_OSNULL) || (ospvEnrollParamsIn->CACertB64Len <= 0)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "Invalid arguments passed in for decoding the CA certificate.\n");
}
/*
* If ( the input parameters look ok ) then
* o set the maximum length of the output CA certificate to be twice
* that of the base64-encoded cert.
* o allocate the memory for the binary cert.
* o set an error code if we couldn't allocate it.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
caCertLen = 2 * ospvEnrollParamsIn->CACertB64Len;
OSPM_MALLOC(caCert, unsigned char, caCertLen + 1);
if (caCert == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for the CA cert.\n");
}
}
/*
* If ( we could allocate the memory ) then
* o initialize its contents
* o try base64 decoding the certificate into the newly-allocated memory
* o complain if the decoding didn't work.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MEMSET(caCert, 0, caCertLen + 1);
retVal = OSPPBase64Decode((const char *)ospvEnrollParamsIn->CACertB64, ospvEnrollParamsIn->CACertB64Len, caCert, &caCertLen);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to base64-decode CA cert.\n");
}
}
/*
* Assign the certificates to the outgoing enrollment parameters if
* the decoding worked.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
ospvEnrollParamsIn->CACert = caCert;
ospvEnrollParamsIn->CACertLen = caCertLen;
}
/*
* Free up the memory allocated ( if it was allocated ) if anything
* went wrong. A better approach might be to allocate fresh memory for
* the outgoing CACert and then deallocate the memory for the caCert
* regardless of the return value.
*/
if (retVal != OSPC_ERR_NO_ERROR) {
if (caCert != OSPC_OSNULL) {
OSPM_FREE(caCert);
}
}
return retVal;
}
/*
* Create an enrollment request for requesting or retrieving a certificate,
* send the request to the enrollment server ( using the communications
* manager passed in ); extract the status and certificate from the
* response, validate the certificate, and return the certificate.
*
* Input: pointer to a communications manager for transmitting the request;
* a pointer to a MessageInfo ( OSPT_MSG_INFO* ) that should contain
* the request; a pointer to the subjectPublicKeyInfo of the
* request ( for validation ); a pointer to an integer for returning
* the status of the request; and a pointer to a (null) string for
* returning the certificate.
*
* Output: *ospvEnrollStatusOut and *ospvCertOut may contain the status
* and certificate for the device if everything went ok. That is,
* if the enrollment server could be contacted and it serviced the
* request, the the status and possibly the certificate should
* be returned. In this case, OSPC_ERR_NO_ERROR should be returned
* as well. Otherwise, an error code other than OSPC_ERR_NO_ERROR
* will be returned and the status and certificate are
* potentially useless.
*/
int OSPPEnrollDevice(OSPTCOMM *ospvCommMgrIn,
OSPT_MSG_INFO *ospvEnrollMsgIn,
OSPTASN1OBJECT *ospvRequestPublicKeyIn,
unsigned *ospvEnrollStatusOut,
unsigned char **ospvCertOut,
unsigned *ospvCertLenOut)
{
int retVal = OSPC_ERR_NO_ERROR;
/*
* The security manager for the communication manager; it keeps track
* of certificates used and so on:
*/
OSPTSEC *commMgrSec = OSPC_OSNULL;
/*
* The status of the certificate request that we retrieve from teh
* enrollment server's response:
*/
unsigned char *statusStr = OSPC_OSNULL;
/*
* The base64-encoded cert that we retrieve from the enrollment
* server's response:
*/
unsigned char *certb64Buf = OSPC_OSNULL;
/* The status of the request: */
unsigned status = OSPC_ENROLL_STATUS_FAILURE_DEFAULT;
/*
* This will represent the null-terminated response received from the
* enrollment server; the response that we get from the communications
* manager isn't null-terminated, which may otherwise introduce problems
* with reading non-heap memory with common functions:
*/
unsigned char *enrollResponse = OSPC_OSNULL;
/*
* Check the parameters; if any of them are null, then set an
* error code and complain.
*/
if ((ospvCommMgrIn == OSPC_OSNULL) || (ospvEnrollMsgIn == OSPC_OSNULL) || (ospvEnrollStatusOut == OSPC_OSNULL) ||
(ospvCertOut == OSPC_OSNULL) || (ospvCertLenOut == OSPC_OSNULL))
{
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The parameters for enrolling a device are invalid.\n");
}
/*
* Now add the enrollment transaction to the communication manager's
* message queue and complain if we have any problems doing it:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("Enrollment request: <%d> bytes: <%s>\n", ospvEnrollMsgIn->RequestSz, ospvEnrollMsgIn->RequestMsg));
OSPM_DBGMISC(("Adding enrollment message to transaction queue.\n"));
retVal = OSPPCommAddTransaction(ospvCommMgrIn, ospvEnrollMsgIn);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to add the enrollment request to the communications manager.\n");
}
}
/*
* We may have gotten a valid return value for adding a request to the
* communication manager's message queue even if the server rejected it.
* So, we'll check the error code of the message here to make sure that
* we could in fact send it:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = ospvEnrollMsgIn->ErrorCode;
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to transmit request to the enrollment server.\n");
}
}
/*
* We'll need a copy of the response message that's null-terminated in
* order to use the parsing mechanism. Another solution would be to
* rewrite the string parser and every other piece of code that uses
* the response to avoid using strlen, strcpy, and any other function
* that may potentially access characters outside the bounds of the
* non-null-terminated response, but that isn't done here.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MALLOC(enrollResponse, unsigned char, ospvEnrollMsgIn->ResponseSz + 1);
/* Complain if the memory isn't available: */
if (enrollResponse == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for null-terminated response.\n");
}
}
/*
* Now we can create a null-terminated version of the response.
* Then look for the status and the certificate that should be returned
* from the enrollment server within the null-terminated response.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
/*
* Create the null-terminated response by zeroing out everything
* and then copying in the string that fits it:
*/
OSPM_MEMSET(enrollResponse, 0, ospvEnrollMsgIn->ResponseSz + 1);
OSPM_STRNCPY(enrollResponse, ospvEnrollMsgIn->ResponseMsg, ospvEnrollMsgIn->ResponseSz);
/* Get the status code from the response: */
retVal = OSPPExtractFieldFromResponse(enrollResponse, ospvEnrollMsgIn->ResponseSz, (unsigned char *)OSPC_ENROLL_STATUS_RSP_PARAM, &statusStr);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to find the cert request status in the response.\n");
}
}
/*
* Check to make sure that the status is a string of digits. Since atoi
* returns a 0 for strings that are invalid, we will interpret a garbled
* message the same way that we'll interpret a successful enrollment if
* we just use atoi first.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("status: <%s>\n", statusStr));
retVal = OSPPValidateDigitString(statusStr);
if (retVal == OSPC_ERR_NO_ERROR) {
status = OSPM_ATOI((const char *)statusStr);
} else {
OSPM_DBGERRORLOG(retVal, "Invalid status returned - assuming cert request failed.\n");
}
}
/*
* Now check the status code that was returned. If the status is
* equal to a code that says that the request was successful, then
* look for the certificate in the response. If the status is equal to
* a code that says that the request is pending approval, then do
* nothing. If the status is equal to anything else, then print out
* an error.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("status returned: <%d>\n", status));
*ospvEnrollStatusOut = status;
if (status == OSPC_ENROLL_STATUS_OK) {
retVal = OSPPExtractFieldFromResponse(enrollResponse, ospvEnrollMsgIn->ResponseSz, (unsigned char *)OSPC_ENROLL_CERT_RSP_PARAM, &certb64Buf);
if (retVal == OSPC_ERR_NO_ERROR) {
commMgrSec = OSPPCommGetSecurity(ospvCommMgrIn);
if (commMgrSec == OSPC_OSNULL) {
OSPM_DBGMISC(("Warning: Unable to validate the certificate returned.\n"));
}
}
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPValidateDeviceCert(commMgrSec, ospvRequestPublicKeyIn, certb64Buf, ospvCertOut, ospvCertLenOut);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_INVALID_RESPONSE;
OSPM_DBGERRORLOG(retVal, "Unable to extract certificate from response indicating success.\n");
}
}
}
/* Nothing to do in this case. */
else if (status == OSPC_ENROLL_STATUS_PENDING) {
OSPM_DBGMISC(("Enrollment status pending\n"));
}
/*
* It looks like the request failed for some reason. We can try to
* decode it, but we definitely need to tell the user what the error
* code is. This code should be propagated to a customer service rep
* or someone who can tell the user what this error indicates, so
* more diagnostics output would be nice.
*/
else {
OSPM_DBGMISC(("Warning: Failure code received from server: %d\n", *ospvEnrollStatusOut));
}
}
/* Now free everything up: */
if (statusStr != OSPC_OSNULL) {
OSPM_FREE(statusStr);
}
/*
* The certificate should be contained in the ospvCertOut field
* if it was successfully obtained, so this is no longer useful:
*/
if (certb64Buf != OSPC_OSNULL) {
OSPM_FREE(certb64Buf);
}
/* The null-terminated response is no longer necessary: */
if (enrollResponse != OSPC_OSNULL) {
OSPM_FREE(enrollResponse);
}
return retVal;
}
/*
* Given a certificate for a device that has been received, validate it
* against the public key used ( to be added ), the CA certificate that
* we expected to sign it, and its general construction. The certificate
* is placed in *ospvCertOut.
*
* Input:
* o ospvSecIn: contains the authority certificate, and will be used for
* validating the CA certificate used for signing the request.
* o ospvRequestPublicKeyIn: contains the public key that was contained in
* the certificate request.
* o ospvCertB64In: the certificate to be taken apart and validated.
*
* Output:
* o ospvCertOut: a pointer to the unsigned char* that will ultimately
* contain the certificate that was transmitted.
*
* - the return value is OSPC_ERR_NO_ERROR( 0 ) if the certificate is ok,
* or non-zero if there's an error.
*
* Errors: Errors are generated when:
* o any of the parameters are null;
* o the CA certificate(s) in the OSPTSEC object are not the signers of
* the received certificate;
* o the public key of the received certificate does not match the one
* that was requested;
* o the certificate received was improperly base64-encoded.
*/
int OSPPValidateDeviceCert(OSPTSEC *ospvSecIn,
OSPTASN1OBJECT *ospvRequestPublicKeyIn,
unsigned char *ospvCertB64In,
unsigned char **ospvCertOut,
unsigned *ospvCertLenOut)
{
int retVal = OSPC_ERR_NO_ERROR;
int certb64BufLen = -1;
/*
* This object will be used for validating that the certificate
* passed in is in fact an X.509 certificate:
*/
OSPTASN1OBJECT *x509CertOut = OSPC_OSNULL;
/* This is the temporary variable for holding the outgoing subjectPublicKeyInfo: */
OSPTASN1OBJECT certOutPublicKey;
/* This is the index of the root CA certificate within the CA cert chain: */
int caCertIndex = -1;
OSPM_DBGENTER(("ENTER: OSPPValidateDeviceCert\n"));
/*
* If ( the parameters are ok ) then
* o get the size of the base64-encoded cert and set the size of
* the binary cert to be twice that ( this is only needed for the
* base64 decoding; in reality the base64-decoded message should be
* no more than 4/3 the size of the base64-encoded message. )
* o Allocate the memory for the output certificate
*/
if ((ospvCertB64In == OSPC_OSNULL) || (ospvCertOut == OSPC_OSNULL) || (ospvCertLenOut == OSPC_OSNULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "Invalid parameters for validating a device cert.\n");
}
if (retVal == OSPC_ERR_NO_ERROR) {
certb64BufLen = OSPM_STRLEN((const char *)ospvCertB64In);
*ospvCertLenOut = 2 * certb64BufLen;
OSPM_MALLOC(*ospvCertOut, unsigned char, *ospvCertLenOut + 1);
/*
* If ( the memory couldn't be allocated ) then
* o we're in trouble; set the error code to indicate that no
* memory could be allocated.
*/
if (*ospvCertOut == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to allocate memory");
}
}
/*
* Else ( everything's ok ) so
* o decode the base64 encoding of the certificate and put it into
* ospvCertOut and its length in the certificate's output length.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MEMSET(*ospvCertOut, 0, *ospvCertLenOut + 1);
retVal = OSPPBase64Decode((const char *)ospvCertB64In, certb64BufLen, *ospvCertOut, ospvCertLenOut);
/* If ( there was a problem ) then set the error code to indicate the certificate received was bogus. */
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to base64-decode certificate\n");
}
}
/* If ( everything's ok ) then Check the certificate's public key and CA certificate. */
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("creating x509 cert..\n"));
retVal = OSPPX509CertCreate(*ospvCertOut, &x509CertOut);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Invalid X.509 certificate returned.\n");
}
}
/*
* Now get the public key out of the certificate that was
* retrieved so that we can compare it to the public key that
* was used in the certificate request.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("getting public key info..\n"));
retVal = OSPPGetPublicKeyInfoFromCert(x509CertOut, &certOutPublicKey);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to retrieve public key from certificate retrieved.\n");
}
}
/* Now compare the two public keys: */
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("comparing asn1..\n"));
retVal = OSPPASN1Compare(&certOutPublicKey, ospvRequestPublicKeyIn);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Public Key in certificate does not match public key in request.\n");
}
}
/*
* The public key is ok, so now make sure that the authority
* certificate ( or CA certificate ) is in fact the certificate
* used for signing the certificate we received. If not, then
* we'll fail the enrollment.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("validating cert..\n"));
retVal = OSPPX509CertValidateCertificate(x509CertOut, ospvSecIn->AuthorityCertInfo, ospvSecIn-> NumberOfAuthorityCertificates, &caCertIndex);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "The certificate returned was not signed by the given CA certificate.\n");
}
}
/*
* Now let's make sure that the contents are valid. Let's decode it and make
* sure that the certificate is signed by the CA certificate. If it's not,
* then we'll set an error code and return. If it is, then we'll need to
* check the public key ( although that's not done right now, but it should be. )
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("certificate returned:\n"));
OSPPDumpHex(x509CertOut->ElementInfo->Element,
x509CertOut->ElementInfo->ElementLength);
}
/* Delete the X.509 cert object; we only needed it for validation: */
if (x509CertOut != OSPC_ERR_NO_ERROR) {
OSPPASN1ObjectDelete(&x509CertOut);
}
/*
* Delete the element info and parse results of the certOutPublicKey,
* which was only needed as a comparison tool for the subjectPublicKeyInfo
* in the certificate request.
*/
if (certOutPublicKey.ElementInfo != OSPC_OSNULL) {
OSPPASN1ElementDelete(&(certOutPublicKey.ElementInfo), 0);
}
if (certOutPublicKey.ParseResults != OSPC_OSNULL) {
PTPResultsDelete(&(certOutPublicKey.ParseResults));
}
OSPM_DBGMISC(("done!\n"));
OSPM_DBGEXIT(("EXIT: OSPPValidateDeviceCert\n"));
return retVal;
}
/*
* Compare the two ASN1 objects. Returns OSPC_ERR_NO_ERROR if they're the
* same, and a non-zero value otherwise.
*
* Input:
* ospvLHSObject: a pointer to an ASN1 Object ( the LeftHand Side )
* ospvRHSObject: a pointer to an ASN1 Object ( the RightHand Side )
*
* Output:
* OSPC_ERR_NO_ERROR if the two are the same, or some other value otherwise.
*
* Errors: Errors will be returned when
* o at least one of the parameters is null ( OSPC_ERR_ENROLL_INVALID_PARAMS )
* o the two have different lengths ( OSPC_ERR_ENROLL_LENGTH_MISMATCH );
* o the two have different contents ( OSPC_ERR_ENROLL_CONTENT_MISMATCH );
*/
int OSPPASN1Compare(OSPTASN1OBJECT *ospvLHSObject,
OSPTASN1OBJECT *ospvRHSObject)
{
int retVal = OSPC_ERR_NO_ERROR;
OSPM_DBGENTER(("ENTER: OSPPASN1Compare\n"));
/* If ( either of the parameters are null ) then set an error code and complain. */
if ((ospvLHSObject == OSPC_OSNULL) || (ospvRHSObject == OSPC_OSNULL) || (ospvLHSObject->ElementInfo == OSPC_OSNULL) ||
(ospvRHSObject->ElementInfo == OSPC_OSNULL) || (ospvLHSObject->ElementInfo->Element == OSPC_OSNULL) ||
(ospvRHSObject->ElementInfo->Element == OSPC_OSNULL))
{
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "At least one of the ASN1 objects being compared is invalid.\n");
if (ospvLHSObject == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The first parameter to OSPPASN1Compare is null.\n");
}
if (ospvRHSObject == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The second parameter to OSPPASN1Compare is null.\n");
}
if (ospvLHSObject->ElementInfo == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The first parameter's element info is null.\n");
}
if (ospvRHSObject->ElementInfo == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The second parameter's element info is null.\n");
}
if (ospvLHSObject->ElementInfo->Element == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The first parameter's element info content is null.\n");
}
if (ospvRHSObject->ElementInfo->Element == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The second parameter's element info content is null.\n");
}
}
/*
* If ( there haven't been any problems ) then
* o if ( the two objects have different lengths ) then
* o set the error code and complain
*/
if (retVal == OSPC_ERR_NO_ERROR) {
if ((ospvLHSObject->ElementInfo)->ElementLength != (ospvRHSObject->ElementInfo)->ElementLength) {
retVal = OSPC_ERR_ENROLL_ASN1_CMP_LENGTH_MISMATCH;
OSPM_DBGERRORLOG(retVal, "The lengths of the two objects compared are different\n");
OSPM_DBGMISC(("Length of content of LHS: %d\n", (ospvLHSObject->ElementInfo)->ElementLength));
OSPM_DBGMISC(("Length of content of RHS: %d\n", (ospvRHSObject->ElementInfo)->ElementLength));
OSPM_DBGMISC(("LHS: \n"));
OSPPDumpHex((ospvLHSObject->ElementInfo)->Element, (ospvLHSObject->ElementInfo)->ElementLength);
OSPPDumpHex((ospvRHSObject->ElementInfo)->Element, (ospvRHSObject->ElementInfo)->ElementLength);
}
}
/*
* If ( there still aren't any errors ) then
* o if ( the two objects have different contents ) then
* o set the error code and complain
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPM_STRNCMP((const char *)((ospvLHSObject->ElementInfo)-> Element), (const char *)((ospvRHSObject->ElementInfo)-> Element),
(ospvLHSObject->ElementInfo)->ElementLength);
if (retVal != 0) {
retVal = OSPC_ERR_ENROLL_ASN1_CMP_MISMATCH;
OSPM_DBGERRORLOG(retVal, "The two ASN1 objects are of unequal value.\n");
}
}
OSPM_DBGEXIT(("EXIT: OSPPASN1Compare\n"));
return retVal;
}
/*
* Given an ASN1 object that represents an X.509 certificate, store its
* subjectPublicKeyInfo in the outbound ospvPublicKeyOut structure.
* This subjectPublicKeyInfo will be compared against what we get from
* the server in the form of a certificate; if they match, then the
* certificate may be ok - otherwise, the certificate is bogus.
*
* Input: references to the input certificate and the outgoing
* subjectPublicKeyInfo
*
* Output: If the subjectPublicKeyInfo can be found, then it should be
* stored in *ospvPublicKeyOut and the return value will be
* OSPC_ERR_NO_ERROR. Otherwise, the return value will be
* something other than OSPC_ERR_NO_ERROR.
*/
int OSPPGetPublicKeyInfoFromCert(OSPTASN1OBJECT *ospvCertIn,
OSPTASN1OBJECT *ospvPublicKeyOut)
{
int retVal = OSPC_ERR_NO_ERROR;
OSPTASN1ELEMENTINFO *publicKeyInfoElementInfo = OSPC_OSNULL;
OSPTASN1PARSERESULT *publicKeyInfoParseResults = OSPC_OSNULL;
if ((ospvCertIn == OSPC_OSNULL) || (ospvPublicKeyOut == OSPC_OSNULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
if (ospvCertIn == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The certificate passed in is null.\n");
}
if (ospvPublicKeyOut == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The public key reference passed in is null.\n");
}
}
/*
* Now parse the certificate's contents for the subjectPublicKeyInfo,
* using the formulation for an X.509 subjectPublicKeyInfo that's
* readily available in the OSP:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPASN1ObjectGetElementByDataRef(ospvCertIn, &publicKeyInfoElementInfo, OSPEDRID_CERT_SUBJPUBKEYINFO);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to find subjectPublicKeyInfo in certificate\n");
retVal = OSPC_ERR_ENROLL_BAD_CERT;
}
}
/* Now generate some parse results so that the element information can be turned into a "complete" OSPTASN1OBJECT: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = PTPResultsCreate(&publicKeyInfoParseResults, publicKeyInfoElementInfo, OSPEDRID_CERT_SUBJPUBKEYINFO);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to reverse-engineer parse results from the given subjectPublicKeyInfo\n");
retVal = OSPC_ERR_ENROLL_ASN1_PARSE;
}
}
/*
* If ( we could finish generating the ASN1 object for the
* subjectPublicKeyInfo ) then
* o finish the generation of the public key by assigning the
* contents we created to the outgoing public key info:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
ospvPublicKeyOut->ElementInfo = publicKeyInfoElementInfo;
ospvPublicKeyOut->ParseResults = publicKeyInfoParseResults;
}
/*
* Else ( we ran into some problems ) so
* o delete the element info and parse results we created:
*/
else {
if (publicKeyInfoElementInfo != OSPC_OSNULL) {
OSPPASN1ElementDelete(&publicKeyInfoElementInfo, 0);
}
if (publicKeyInfoParseResults != OSPC_OSNULL) {
PTPResultsDelete(&publicKeyInfoParseResults);
}
}
return retVal;
}
/*
* Given a binary string that represents a PKCS#10 request, create an
* ASN1 object that contains the subjectPublicKeyInfo of the certificate
* request. The subjectPublicKeyInfo is found as follows:
*
* SEQUENCE -- Certificate request
* SEQUENCE -- Certificate request info
* INTEGER -- version
* SEQUENCE -- subject name
* SEQUENCE -- subject public key info
* SEQUENCE -- optional attributes
* OID -- signature algorithm
* BIT STRING -- signature
*
* We'll use the ASN1 module from the OSP to decode the binary string
* and extract the public key from the certificate request.
*
* Input: string representing a PKCS#10 certificate request, and a pointer
* to an ASN1 object for storing its subjectPublicKeyInfo.
*
* Output: the subjectPublicKeyInfo should be found, in which case we'll
* return OSPC_ERR_NO_ERROR. Otherwise, a different error code will
* be returned.
*/
int OSPPGetPublicKeyInfoFromCertReq(unsigned char *ospvCertReqB64In,
OSPTASN1OBJECT *ospvPublicKeyOut)
{
int retVal = OSPC_ERR_NO_ERROR;
/* This represents the length of the base64-encoded request passed in: */
unsigned certReqB64LenIn = 0;
/*
* This will be the binary for storing the base64-decoded certificate
* request; it should be deleted at the end of this function:
*/
unsigned char *certReqBinary = OSPC_OSNULL;
/* This will store the length of that binary string: */
unsigned certReqBinaryLen = 0;
/* This points to the description of the certificate's structure: */
OSPTASN1ELEMENTINFO *certReqElementInfo = OSPC_OSNULL;
/*
* This is used for linking together all of the ElementInfos into
* a tree-like structure and for validating the certificate request:
*/
OSPTASN1PARSERESULT *certReqParseResults = OSPC_OSNULL;
/*
* This will be the union of the certReqElementInfo and certReqParseResults;
* we'll need it for extracting the subjectPublicKeyInfo.
*/
OSPTASN1OBJECT certReqASN1;
/* This points to the description of the public key's structure: */
OSPTASN1ELEMENTINFO *publicKeyInfoElementInfo = OSPC_OSNULL;
/*
* These will be used for validating the structure of the
* public key as well:
*/
OSPTASN1PARSERESULT *publicKeyInfoParseResults = OSPC_OSNULL;
OSPM_DBGENTER(("ENTER: OSPPGetPublicKeyInfoFromCertReq\n"));
/* See if any of the parameters are null: */
if ((ospvCertReqB64In == OSPC_OSNULL) || (OSPM_STRLEN((const char *)ospvCertReqB64In) <= 0) || (ospvPublicKeyOut == OSPC_OSNULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
if (ospvCertReqB64In == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The cert request is null.\n");
} else if (OSPM_STRLEN((const char *)ospvCertReqB64In) <= 0) {
OSPM_DBGERRORLOG(retVal, "The cert request is empty.\n");
}
if (ospvPublicKeyOut == OSPC_OSNULL) {
OSPM_DBGERRORLOG(retVal, "The public key reference passed in is null.\n");
}
}
/*
* The certificate request should be base64-encoded, so we'll need
* to decode it. First, though, create the memory to store it:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
certReqB64LenIn = OSPM_STRLEN((const char *)ospvCertReqB64In);
certReqBinaryLen = 2 * certReqB64LenIn;
OSPM_MALLOC(certReqBinary, unsigned char, certReqBinaryLen + 1);
if (certReqBinary == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "No memory available for the binary certificate request.\n");
}
}
/* Now perform the base64 decoding: */
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MEMSET(certReqBinary, 0, certReqBinaryLen + 1);
retVal = OSPPBase64Decode((const char *)ospvCertReqB64In, certReqB64LenIn, certReqBinary, &certReqBinaryLen);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to base64-decode the certificate request.\n");
} else if ((OSPM_STRLEN((const char *)certReqBinary) <= 0) || (certReqBinaryLen <= 0)) {
retVal = OSPC_ERR_B64_DECODE_FAILED;
OSPM_DBGERRORLOG(retVal, "Unable to base64-decode the certificate request.\n");
}
}
/*
* Now we'll create a PKCS#10 request object from the ospvCertReqB64In.
* If we can't do it, then the request is bad or misformulated and
* we'll have to stop the enrollment process, anyway. This doesn't
* guarantee that the enrollment server will accept the request,
* but it should be a minimal check against what it's expecting, anyway.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPASN1ElementDecode(certReqBinary, &certReqElementInfo, OSPC_ASN1_DATAREFID_CERTREQ);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to decode the certificate request.\n");
}
}
/*
* Now try to parse the contents of the certificate request to ensure
* that at least a minimal subset of the required elements exist. If
* not, then this function will catch it as it tries to find the
* CertReqInfo, then the signatureAlgorithm, and finally the signature.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPASN1ElementParse(certReqElementInfo, OSPEPTID_CERTREQ, OSPC_OSNULL, &certReqParseResults, OSPC_ASN1_DATAREFID_CERTREQ);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to parse the contents of the certificate request.\n");
}
}
/*
* Now parse the certificate request's contents for the
* subjectPublicKeyInfo, using the formulation for a PKCS#10
* readily available in the OSP:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
certReqASN1.ElementInfo = certReqElementInfo;
certReqASN1.ParseResults = certReqParseResults;
retVal = OSPPASN1ObjectGetElementByDataRef(&certReqASN1, &publicKeyInfoElementInfo, OSPEDRID_CERTREQINFO_SUBJPUBKEYINFO);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to find subjectPublicKeyInfo in cert request\n");
retVal = OSPC_ERR_ENROLL_BAD_CERT_REQ;
}
}
/*
* Now generate some parse results so that the element information can
* be turned into a "complete" OSPTASN1OBJECT:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = PTPResultsCreate(&publicKeyInfoParseResults, publicKeyInfoElementInfo, OSPEDRID_CERT_SUBJPUBKEYINFO);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to reverse-engineer parse results from the given subjectPublicKeyInfo\n");
retVal = OSPC_ERR_ENROLL_ASN1_PARSE;
}
}
/*
* If ( we could finish generating the ASN1 object for the
* subjectPublicKeyInfo ) then
* o finish the generation of the public key by assigning the
* contents we created to the outgoing public key info:
*/
if (retVal == OSPC_ERR_NO_ERROR) {
ospvPublicKeyOut->ElementInfo = publicKeyInfoElementInfo;
ospvPublicKeyOut->ParseResults = publicKeyInfoParseResults;
OSPM_DBGMISC(("public key content: \n"));
OSPPDumpHex(ospvPublicKeyOut->ElementInfo->Element, ospvPublicKeyOut->ElementInfo->ElementLength);
}
/*
* Else ( we ran into some problems ) so
* o delete the publicKeyInfo, element info and parse results we created:
*/
else {
if (ospvPublicKeyOut != OSPC_OSNULL) {
ospvPublicKeyOut->ElementInfo = OSPC_OSNULL;
ospvPublicKeyOut->ParseResults = OSPC_OSNULL;
}
if (publicKeyInfoElementInfo != OSPC_OSNULL) {
OSPPASN1ElementDelete(&publicKeyInfoElementInfo, 0);
}
if (publicKeyInfoParseResults != OSPC_OSNULL) {
PTPResultsDelete(&publicKeyInfoParseResults);
}
}
/* Delete the element info, since we won't need it any more: */
if (certReqElementInfo != OSPC_OSNULL) {
OSPPASN1ElementDelete(&certReqElementInfo, 0);
}
/*
* Else ( the certificate request wasn't successfully parsed into an
* ASN1 structure, so it may still be allocated ) so
* o free up the memory that it would have pointed to ( if necessary )
*/
else {
if (certReqBinary != OSPC_OSNULL) {
OSPM_FREE(certReqBinary);
}
}
/* Delete the parse results as well: */
if (certReqParseResults != OSPC_OSNULL) {
PTPResultsDelete(&certReqParseResults);
}
OSPM_DBGEXIT(("EXIT: OSPPGetPublicKeyInfoFromCertReq\n"));
return retVal;
}
/*
* This is for constructing the enrollment request that is sent to the
* enrollment server; the output is an OSPT_MSG_INFO structure that
* contains it.
*
* Input: A pointer to the enrollment parameters and to the MessageInfo that
* contains the parameters for the enrollment request.
*
* Output: The OSPT_MSG_INFO should contain all of the information necessary
* for transmitting a request to the enrollment server. In this case,
* the return value will be OSPC_ERR_NO_ERROR. If something
* goes wrong, then the return value will be something other than
* OSPC_ERR_NO_ERROR. In that case, there is no guarantee about what
* the OSPT_MSG_INFO* structure will contain.
*/
int OSPPConstructEnrollmentRequest(OSPTENROLLPARAMS *ospvEnrollParamsIn,
OSPT_MSG_INFO *ospvMsgInfoOut)
{
int retVal = OSPC_ERR_NO_ERROR;
/* This is the temporary storage for the enrollment request: */
unsigned char *requestBfr = OSPC_OSNULL;
OSPM_DBGENTER(("OSPPConstructEnrollmentRequest enter\n"));
/* set an error code and complain if there's something wrong with the input parameters: */
if ((ospvEnrollParamsIn == NULL) || (ospvMsgInfoOut == NULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "At least one parameter passed in was null.\n");
}
/*
* If ( the parameters weren't null ) then
* o check the enrollment request parameters for any null or invalid
* values first.
* o complain if there's something wrong.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPCheckEnrollmentParams(ospvEnrollParamsIn);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_INVALID_REQUEST_PARAMS;
OSPM_DBGERRORLOG(retVal, "The cert request's construction parameters were invalid.\n");
}
}
/*
* If ( the enrollment parameters are ok ) then
* o Create the nonce if necessary. The method for creating this
* nonce may not be random enough, so it's advisable to send this
* value in on input. We'll free up any memory that was already
* created for the nonce if it's not null.
* o complain if we couldn't create the nonce.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
if ((ospvEnrollParamsIn->Nonce == OSPC_OSNULL) || (ospvEnrollParamsIn->NonceLen <= 0)) {
retVal = OSPPCreateNonce(&(ospvEnrollParamsIn->Nonce), &(ospvEnrollParamsIn->NonceLen), OSPC_ENROLL_NONCE_LEN);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to create nonce for the enrollment request.\n");
} else if ((ospvEnrollParamsIn->Nonce == OSPC_OSNULL) || (ospvEnrollParamsIn->NonceLen <= 0)) {
retVal = OSPC_ERR_ENROLL_GEN_NONCE;
OSPM_DBGERRORLOG(retVal, "Unable to generate nonce.\n");
}
}
}
/*
* If ( the enrollment parameters look ok ) then
* o generate the body for the enrollment request
* o complain if we had problems.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPCreateEnrollmentRequestBody(&requestBfr, ospvEnrollParamsIn);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to create enrollment request's body.\n");
}
/* check the return values to make sure they're not invalid: */
else if ((requestBfr == OSPC_OSNULL) || (OSPM_STRLEN((const char *)requestBfr) <= 0)) {
retVal = OSPC_ERR_ENROLL_CREATE_REQUEST_BODY;
OSPM_DBGERRORLOG(retVal, "The request body generated was invalid.\n");
}
}
/*
* IF ( the request body could be generated ) then
* o create the enrollment request header.
* o complain if we had problems.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPCreateEnrollmentRequestHeader(ospvMsgInfoOut);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to create enrollment request's header.\n");
}
}
/*
* If ( we could create the body and header without a glitch ) then
* o assign the request to the message info.
* o set the size of the message
*/
if (retVal == OSPC_ERR_NO_ERROR) {
ospvMsgInfoOut->RequestMsg = requestBfr;
ospvMsgInfoOut->RequestSz = OSPM_STRLEN((const char *)requestBfr);
}
/* Else ( we ran into some problem ) so free up the requestBfr if necessary */
else {
if (requestBfr != OSPC_OSNULL) {
OSPM_FREE(requestBfr);
}
}
OSPM_DBGEXIT(("OSPPConstructEnrollmentRequest exit\n"));
return retVal;
}
/*
* Check all of the enrollment parameters for any problems that might cause
* it to be rejected by an enrollment server. We'll check that the
* username and password are alnum, that the customer and device ids
* are numeric, and that the base64-encoded certificate request exists
* ( the contents of the base64-encoded certificate request are validated
* elsewhere. ) We'll get an error if any of these values are empty or null.
*
* Input: reference to the enrollment parameters
*
* Output: OSPC_ERR_NO_ERROR if all of the parameters are non-null, non-empty,
* and have the appropriate type. Otherwise, an error code other
* than OSPC_ERR_NO_ERROR will be returned.
*/
int OSPPCheckEnrollmentParams(OSPTENROLLPARAMS *ospvEnrollParams)
{
int retVal = OSPC_ERR_NO_ERROR;
OSPM_DBGENTER(("ENTER: OSPPCheckEnrollmentParams\n"));
/* Set an error code if the input parameters are null: */
if (ospvEnrollParams == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
}
if (retVal == OSPC_ERR_NO_ERROR) {
if (ospvEnrollParams->Username == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_BAD_USERNAME;
OSPM_DBGERRORLOG(retVal, "The username was null.\n");
}
/* Check the username: */
retVal = OSPPValidateAsciiString(ospvEnrollParams->Username);
OSPM_DBGMISC(("username: <%s>\n", ospvEnrollParams->Username));
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_BAD_USERNAME;
OSPM_DBGERRORLOG(retVal, "An invalid ( non-alnum ) username was entered.\n");
}
}
/* Check the password: */
if (retVal == OSPC_ERR_NO_ERROR) {
if (ospvEnrollParams->Password == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_BAD_PASSWORD;
OSPM_DBGERRORLOG(retVal, "The password was null.\n");
}
retVal = OSPPValidateAsciiString(ospvEnrollParams->Password);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_BAD_PASSWORD;
OSPM_DBGERRORLOG(retVal, "An invalid ( non-alnum ) password was entered.\n");
}
}
/* Check the device id: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPValidateDigitString(ospvEnrollParams->DeviceId);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_BAD_DEVICE_ID;
OSPM_DBGERRORLOG(retVal, "An invalid ( non-digit ) device id was entered.\n");
}
}
/* Check the customer id: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPValidateDigitString(ospvEnrollParams->CustomerId);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_BAD_CUSTOMER_ID;
OSPM_DBGERRORLOG(retVal, "An invalid ( non-digit ) customer id was entered.\n");
}
}
/* check the certificate request: */
if (retVal == OSPC_ERR_NO_ERROR) {
if ((ospvEnrollParams->CertReq == OSPC_OSNULL) || (OSPM_STRLEN((const char *)ospvEnrollParams->CertReq) <= 0)) {
retVal = OSPC_ERR_ENROLL_BAD_CERT_B64;
}
}
OSPM_DBGEXIT(("EXIT: OSPPCheckEnrollmentParams\n"));
return retVal;
}
/*
* Given a pointer to a string to write, and a pointer to its length, and
* the length of a nonce to generate, generate a nonce and place it in the
* output string and its referenced length. The nonce will be binary, not
* ASCII.
*
* This function will use OSPPFillBufWithRandomBytes, which in turn
* relies on OSPPUtilGetRandom, for generating the random bytes of the nonce.
* Since OSPPUtilGetRandom doesn't rely on anything special for the
* entropy of these random values, it would be best to either modify
* OSPPUtilGetRandom or just pass the value of the nonce in ( so that we
* don't generate it here. )
*
* Input: reference to a string and its length to write to, as well as a
* length that specifies how long the nonce should be.
*
* Output: OSPC_ERR_NO_ERROR if there aren't any problems; in this case,
* the *ospvNonceOut should be non-null and *ospvNonceLenOut should
* specify its length. Otherwise, an error code other than
* OSPC_ERR_NO_ERROR will be returned if a problem comes up. In this
* case, *ospvNonceOut should be OSPC_OSNULL and *ospvNonceLenOut
* should be 0 ( but this isn't guaranteed, especially if
* ospvNonceOut or ospvNonceLenOut were OSPC_OSNULL to begin with. )
*/
int OSPPCreateNonce(unsigned char **ospvNonceOut,
unsigned *ospvNonceLenOut,
unsigned ospvNonceLenIn)
{
int retVal = OSPC_ERR_NO_ERROR;
OSPM_DBGENTER(("ENTER: OSPPCreateNonce\n"));
/* Check the input parameters for any problems: */
if ((ospvNonceOut == OSPC_OSNULL) || (ospvNonceLenOut == OSPC_OSNULL) || (ospvNonceLenIn <= 0)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "Bad arguments for creating a nonce.\n");
}
/*
* If ( we had didn't have any problems ) then
* o allocate space for the outgoing nonce
* o complain if we couldn't create the space.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
/* Start by freeing up space so that we don't get a memory leak: */
if (*ospvNonceOut != OSPC_OSNULL) {
OSPM_FREE(*ospvNonceOut);
}
OSPM_MALLOC(*ospvNonceOut, unsigned char, ospvNonceLenIn + 1);
if (*ospvNonceOut == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to alloc memory for nonce.\n");
}
}
/*
* If ( we could allocate memory for the nonce ) then
* o initialize it.
* o fill it with random bytes
* o complain if the return value is bad or if the output nonce or
* length are empty.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MEMSET(*ospvNonceOut, 0, ospvNonceLenIn + 1);
retVal = OSPPFillBufWithRandomBytes(*ospvNonceOut, ospvNonceLenOut, ospvNonceLenIn);
if ((retVal != OSPC_ERR_NO_ERROR) || (*ospvNonceOut == OSPC_OSNULL) || (*ospvNonceLenOut <= 0)) {
retVal = OSPC_ERR_ENROLL_GEN_NONCE;
OSPM_DBGERRORLOG(retVal, "Unable to generate nonce.\n");
}
}
return retVal;
}
/*
* Create the enrollment request body that will be sent to the enrollment
* server. We'll need the enrollment parameters passed in on the command
* line as well as the random bytes that constitute the nonce. The
* request buffer should be alphanumeric, so we'll need the string ( but
* not a pointer to its length. )
*
* Input: the buffer to write the body to; the nonce and its length ( since
* the nonce may be binary ); and the enrollment parameters.
*
* Output: OSPC_ERR_NO_ERROR if everything goes ok; in this case, the buffer
* should contain a request that can be transmitted as part of an
* HTTP POST to the enrollment server. Otherwise, an error code will
* be returned and the buffer will be worthless.
*/
int OSPPCreateEnrollmentRequestBody(unsigned char **ospvRequestBfrOut,
OSPTENROLLPARAMS *ospvEnrollParamsIn)
{
int retVal = OSPC_ERR_NO_ERROR;
/*
* This is the maximum size of the request being generated; it
* will be used for allocating memory later on.
*/
int requestBfrSize = 0;
OSPM_DBGENTER(("ENTER: OSPPCreateEnrollmentRequestBody\n"));
/* Check the parameters first: */
if (ospvRequestBfrOut == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The buffer for the enrollment request's body was null.\n");
}
/* Now check the enrollment parameters: */
if (retVal == OSPC_ERR_NO_ERROR) {
if (ospvEnrollParamsIn == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The enrollment parameters for the enrollment request were null.\n");
}
}
/* Check the enrollment request parameters: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPCheckEnrollmentParams(ospvEnrollParamsIn);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "The enrollment request parameters are invlaid.\n");
}
}
/*
* Now check the nonce for a non-null value; this isn't done in
* OSPPCheckEnrollParams because the nonce may not have been
* entered on the command line.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
if ((ospvEnrollParamsIn->Nonce == OSPC_OSNULL) ||
(ospvEnrollParamsIn->NonceLen <= 0)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The nonce for the enrollment request was null or empty.\n");
}
}
/*
* Allocate enough memory to store all of these values. We'll need
* enough memory to hold all of the url-encoded characters. The maximum
* size is then 3 times the size of all of the parameters combined
* ( since each character may need to be url-encoded. )
*/
if (retVal == OSPC_ERR_NO_ERROR) {
requestBfrSize = 3 * (ospvEnrollParamsIn->NonceLen + OSPM_STRLEN((const char *)ospvEnrollParamsIn->Username) +
OSPM_STRLEN((const char *)ospvEnrollParamsIn->Password) + OSPM_STRLEN((const char *)ospvEnrollParamsIn->DeviceId) +
OSPM_STRLEN((const char *)ospvEnrollParamsIn->CustomerId) + OSPM_STRLEN((const char *)ospvEnrollParamsIn->CertReq) +
OSPM_STRLEN((const char *)ospvEnrollParamsIn->Function) + 100);
OSPM_MALLOC(*ospvRequestBfrOut, unsigned char, requestBfrSize + 1);
if (*ospvRequestBfrOut == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for the outbound request.\n");
}
}
/* Now add the values; add the requested operation first: */
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MEMSET(*ospvRequestBfrOut, 0, requestBfrSize + 1);
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_OPERATION_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_OPERATION_REQ_PARAM), ospvEnrollParamsIn->Function,
OSPM_STRLEN((const char *)ospvEnrollParamsIn->Function), OSPC_FALSE);
OSPM_PRINTTOERR((stderr, "\noutput buffer after operation: %s\n", *ospvRequestBfrOut));
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal, "Unable to append operation to request body.\n");
}
}
/* Now add the nonce: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_NONCE_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_NONCE_REQ_PARAM), ospvEnrollParamsIn->Nonce, ospvEnrollParamsIn->NonceLen, OSPC_TRUE);
OSPM_PRINTTOERR((stderr, "output buffer after nonce: %s\n", *ospvRequestBfrOut));
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal, "Unable to append nonce to request body.\n");
}
}
/* Now add the username: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_USERNAME_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_USERNAME_REQ_PARAM), ospvEnrollParamsIn->Username,
OSPM_STRLEN((const char *)ospvEnrollParamsIn->Username), OSPC_TRUE);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal, "Unable to append username to request body.\n");
}
}
/* Now add the password: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_PASSWORD_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_PASSWORD_REQ_PARAM), ospvEnrollParamsIn->Password,
OSPM_STRLEN((const char *)ospvEnrollParamsIn->Password), OSPC_TRUE);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal, "Unable to append password to request body.\n");
}
}
/* Now add the device id: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_DEVICEID_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_DEVICEID_REQ_PARAM), ospvEnrollParamsIn->DeviceId,
OSPM_STRLEN((const char *)ospvEnrollParamsIn->DeviceId), OSPC_TRUE);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal, "Unable to append device id to request body.\n");
}
}
/* Now add the customer id: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_CUSTOMERID_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_CUSTOMERID_REQ_PARAM), ospvEnrollParamsIn->CustomerId,
OSPM_STRLEN((const char *)ospvEnrollParamsIn->CustomerId), OSPC_TRUE);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal, "Unable to append customer id to request body.\n");
}
}
/* Now add the certificate request: */
if (retVal == OSPC_ERR_NO_ERROR) {
retVal = OSPPAddNameValuePair(*ospvRequestBfrOut, (unsigned char *)OSPC_ENROLL_CERT_REQ_PARAM,
OSPM_STRLEN(OSPC_ENROLL_CERT_REQ_PARAM), ospvEnrollParamsIn->CertReq,
OSPM_STRLEN((const char *)ospvEnrollParamsIn->CertReq), OSPC_TRUE);
if (retVal != OSPC_ERR_NO_ERROR) {
retVal = OSPC_ERR_ENROLL_APPENDING_NAME_VALUE_PAIR;
OSPM_DBGERRORLOG(retVal,
"Unable to append certificate request to request body.\n");
}
}
/*
* If ( we couldn't generate the body ) then
* o make sure that we free up the memory, since the caller might
* not ( especially if the return value indicates otherwise. )
*/
if (retVal != OSPC_ERR_NO_ERROR) {
if (*ospvRequestBfrOut != OSPC_OSNULL) {
OSPM_FREE(*ospvRequestBfrOut);
}
}
OSPM_DBGEXIT(("EXIT: OSPPCreateEnrollmentRequestBody\n"));
return retVal;
}
/*
* Create the enrollment request header that will be sent to the enrollment
* server. We'll just set the content type here.
*
* Input: A pointer to the MessageInfo structure that will contain the
* enrollment request ( and a pointer to the request's header. )
*
* Output: OSPC_ERR_NO_ERROR if everything goes ok; in this case, the
* ospvEnrollReqMsgInfo->ContentType should be set as well.
* Otherwise, an errorcode should be returned and
* ospvEnrollReqMsgInfo->ContentType should be null or empty.
*/
int OSPPCreateEnrollmentRequestHeader(OSPT_MSG_INFO *ospvEnrollReqMsgInfo)
{
int retVal = OSPC_ERR_NO_ERROR;
OSPM_DBGENTER(("ENTER: OSPPCreateEnrollmentRequestHeader\n"));
if (ospvEnrollReqMsgInfo == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "Parameter was null.\n");
}
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_MALLOC(ospvEnrollReqMsgInfo->ContentType, unsigned char, OSPM_STRLEN(OSPC_ENROLL_CONTENT_TYPE) + 1);
if (ospvEnrollReqMsgInfo->ContentType == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for content type.\n");
}
}
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_STRCPY((char *)ospvEnrollReqMsgInfo->ContentType, OSPC_ENROLL_CONTENT_TYPE);
}
OSPM_DBGEXIT(("EXIT: OSPPCreateEnrollmentRequestHeader\n"));
return retVal;
}
/*
* Create a nonce value that will serve for both stronger encryption ( to
* eliminate known-ciphertext attacks ) and for preventing flooding at the
* server. This isn't required by an enrollment server, but it is recommended.
*
* Input: references to the nonce's character string and the nonce's length;
* both are set on output.
*
* Output: success in generating the nonce: OSPC_ERR_NO_ERROR if successful,
* another value otherwise. *ospvRandomByteBuf should be allocated a block
* of memory whose length is specified by ospvRandomByteBufLenIn. Note that
* existing memory will not be used.
*
*/
int OSPPFillBufWithRandomBytes(unsigned char *ospvRandomByteBuf,
unsigned *ospvBytesCreated,
unsigned ospvBytesRequest)
{
int retVal = 0;
/* How many bytes have been put into the nonce so far? */
int bytesFilled = 0;
/* How many bytes are needed to fill the nonce? */
int bytesToBeFilled = 0;
/* How many bytes were created in the last call to OSPPGetRandomBytes? */
int numBytesCreated = 0;
/* How many bytes should be copied? We may need less if we're going to potentially overflow ospvRandomByteBuf. */
int bytesToCopy = 0;
/* How many more bytes are needed for the buffer? Equal to bytesToBeFilled - bytesFilled */
int bytesRemainingToFill = 0;
/* What do we seed the rng with? */
int seed = 0;
/*
* We don't know in advance just how many bytes we're going to
* get, so make sure that we just use a buffer and then copy
* over the appropriate amount.
*/
unsigned char *tempRandomBytes = OSPC_OSNULL;
OSPM_DBGENTER(("ENTER: OSPPFillBufWithRandomBytes\n"));
/* If ( the parameters are invalid ) then set an error code and complain. */
if ((ospvRandomByteBuf == OSPC_OSNULL) || (ospvBytesCreated == OSPC_OSNULL) || (ospvBytesRequest <= 0)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "Invalid arguments for generating random bytes.\n");
}
if (retVal == OSPC_ERR_NO_ERROR) {
/*
* Allocate the temporary storage; make sure that it will accommodate
* as many bytes as may be returned. Each byte may have 3 digits, so
* we will just need 3 bytes for every byte in an unsigned ( plus 1. )
*/
OSPM_MALLOC(tempRandomBytes, unsigned char, 3 * sizeof(unsigned) + 1);
if (tempRandomBytes == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_NO_MEMORY;
OSPM_DBGERRORLOG(retVal, "Unable to allocate memory for random bytes.\n");
}
}
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_DBGMISC(("Able to allocate memory for random buf.\n"));
OSPM_MEMSET(tempRandomBytes, 0, 3 * sizeof(unsigned) + 1);
/*
* Initialize the loop constraints: bytesToBeFilled will be fixed,
* but bytesFilled will increase as more bytes are added:
*/
bytesToBeFilled = ospvBytesRequest;
bytesFilled = 0;
/*
* While ( we still have bytes to fill ) do
* o create some random bytes; the number created is recorded
* in numBytesCreated. seed should be the seed:
* o if ( the number of bytes made will overflow the nonce ) then
* - set the number of bytes to copy to just be the number
* of bytes that can be filled.
* o else ( it's safe to just copy everything ) so
* - set the number of bytes to copy to be the number of
* bytes generated.
* o now copy the appropriate number of bytes
* o update the seed ( this does not have a lot of entropy, and
* it is based off of all of the prior seeds indirectly )
* o update the number of bytes copied.
*/
bytesRemainingToFill = bytesToBeFilled;
while (bytesRemainingToFill > 0) {
numBytesCreated = OSPPUtilGetRandom((char *)tempRandomBytes, seed);
if (numBytesCreated > bytesRemainingToFill) {
bytesToCopy = bytesRemainingToFill;
} else {
bytesToCopy = numBytesCreated;
}
OSPM_STRNCPY(ospvRandomByteBuf + bytesFilled, tempRandomBytes, bytesToCopy);
seed += bytesFilled + bytesToCopy + ospvRandomByteBuf[bytesFilled];
bytesFilled += bytesToCopy;
bytesRemainingToFill = bytesToBeFilled - bytesFilled;
}
OSPM_DBGMISC(("bytesFilled: <%d>\n", bytesFilled));
}
/* Now finish the operation by assigning the bytes created to the output buffer: */
if (retVal == OSPC_ERR_NO_ERROR) {
*ospvBytesCreated = bytesFilled;
}
/* Free up the temporary memory we created; the same bytes should be ospvRandomByteBuf: */
if (tempRandomBytes != OSPC_OSNULL) {
OSPM_FREE(tempRandomBytes);
}
OSPM_DBGEXIT(("EXIT OSPPFillBufWithRandomBytes\n"));
return retVal;
}
/*
* Given a MsgInfo structure that will eventually be sent to an enrollment
* server, add the given name-value pair to the MsgInfo's RequestMsg field.
* We may want to add a name, but not a value. Therefore, it is ok for the
* ospvValue parameter to be OSPC_OSNULL.
*
* Input: a pointer to the MsgInfo being updated, as well as the name
* and value being added.
*
* Output: OSPC_ERR_NO_ERROR if the name-value pair can be added;
* otherwise, an error.
*/
int OSPPAddNameValuePair(unsigned char *ospvDestStr,
unsigned char *ospvName,
unsigned ospvNameLen,
unsigned char *ospvValue,
unsigned ospvValueLen,
unsigned ospvPrependAmpersand)
{
int retVal = OSPC_ERR_NO_ERROR;
/* If ( the destination string or name to be appended are null ) then set an error code and complain. */
if ((ospvDestStr == OSPC_OSNULL) || (ospvName == OSPC_OSNULL) || (ospvValue == OSPC_OSNULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The parameters for appending a name-value pair are invalid.\n");
}
/*
* If ( the parameters are ok ) then
* o add an ampersand to the beginning if necessary;
* o append the name as a url-encoded string
* o complain if we couldn't append the name
*/
if (retVal == OSPC_ERR_NO_ERROR) {
/* Add an ampersand, but only if the flag was set: */
if (ospvPrependAmpersand) {
OSPM_STRNCAT((char *)ospvDestStr, OSPC_ENROLL_FIELD_DELIMITER, 1);
}
retVal = OSPPAppendUrlEncodedString(ospvDestStr, ospvName, ospvNameLen);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to append the url-encoded name in the name-value pair.\n");
}
}
/*
* If ( the name could be added ) then
* o add the name-value delimiter to the string
* o add the value
* o complain if we had any problems.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
OSPM_STRNCAT((char *)ospvDestStr, OSPC_ENROLL_NAME_VALUE_DELIMITER, OSPM_STRLEN(OSPC_ENROLL_NAME_VALUE_DELIMITER));
retVal = OSPPAppendUrlEncodedString(ospvDestStr, ospvValue, ospvValueLen);
if (retVal != OSPC_ERR_NO_ERROR) {
OSPM_DBGERRORLOG(retVal, "Unable to append the url-encoded value in the name-value pair.\n");
}
}
return retVal;
}
/*
* Given a source string, its length, and a destination string,
* url-encode that string and append to the destination string.
*
* Input: the string to be appended to, the string being appended, and
* the length of that string.
*
* Output: the destination string should have the url-encoded source string
* appended to it if there are no problems. In this case, the
* return value should be OSPC_ERR_NO_ERROR. Otherwise, an error
* code other than OSPC_ERR_ENROLL_INVALID_ARG will be returned.
*/
int OSPPAppendUrlEncodedString(unsigned char *destStr,
unsigned char *srcStr,
unsigned srcStrLen)
{
int retVal = OSPC_ERR_NO_ERROR;
/* Index for parsing the source string: */
unsigned srcStrIndex = 0;
/* Offset into the destination string for appending: */
int destStrIndex = 0;
/* The next character from the source string: */
unsigned nextChar = 0;
OSPM_DBGENTER(("ENTER: OSPPAppendUrlEncodedString\n"));
if ((destStr == OSPC_OSNULL) && (srcStr == OSPC_OSNULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The source or destination string is null.\n");
}
/*
* If ( the parameters are non-null ) then
* o get the length of the destination string
* o for ( every character in the source string )
* - if ( the next character is alphanumeric ) then
* o append it directly to the destination string
* - else
* o url-encode the character before appending it.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
/*
* Measure the length of the destination string, so that we know
* where to start appending:
*/
destStrIndex = OSPM_STRLEN((const char *)destStr);
for (srcStrIndex = 0; srcStrIndex < srcStrLen; srcStrIndex++) {
nextChar = srcStr[srcStrIndex];
if (OSPM_ISALNUM(nextChar)) {
destStr[destStrIndex++] = (unsigned char) nextChar;
} else {
destStr[destStrIndex++] = '%';
sprintf((char *)destStr + destStrIndex, "%.2X", nextChar);
destStrIndex += 2;
}
}
}
OSPM_DBGEXIT(("EXIT: OSPPAppendUrlEncodedString\n"));
return retVal;
}
/*
* Given an alnum string, make sure that it's valid: it's non-null,
* and has nothing but alnum characters.
*
* Input: character string to be scanned
* Output: OSPC_ERR_NO_ERROR if it's a non-null alnum string, or
* a non-zero value otherwise:
*/
int OSPPValidateAsciiString(unsigned char *ospvAsciiStr)
{
int retVal = OSPC_ERR_NO_ERROR;
int strIndex = 0;
int asciiStrLen = 0;
OSPM_DBGENTER(("ENTER: OSPPValidateAsciiString\n"));
/* check the string before we try to parse it: */
if (ospvAsciiStr == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "Invalid parameter for checking an ascii string\n");
}
if (retVal == OSPC_ERR_NO_ERROR) {
asciiStrLen = OSPM_STRLEN((const char *)ospvAsciiStr);
if (asciiStrLen <= 0) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The ascii string passed in was empty");
}
}
/*
* Now loop through each character; if it's not ascii, then
* set a flag that indicates that the string isn't ascii.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
for (strIndex = 0; (strIndex < asciiStrLen) && (retVal == OSPC_ERR_NO_ERROR); strIndex++) {
if (OSPM_ISASCII(ospvAsciiStr[strIndex]) == 0) {
retVal = OSPC_ERR_BAD_ASCII_STR;
OSPM_DBGMISC(("<%c> is not an ascii character\n", ospvAsciiStr[strIndex]));
}
}
}
OSPM_DBGEXIT(("EXIT: OSPPValidateAsciiString\n"));
return retVal;
}
/*
* Given a string of digits, make sure that it's valid: it's non-null,
* and has nothing but digits.
*
* Input: character string to be scanned
* Output: OSPC_ERR_NO_ERROR if it's a non-null string of digits, or
* an error otherwise.
*
* Errors:
* OSPC_ERR_ENROLL_BAD_DIGIT_STR: non-null, non-empty string that contains
* non-digit characters.
* OSPC_ERR_ENROLL_INVALID_ARG: a null or empty string was passed in.
*/
int OSPPValidateDigitString(unsigned char *ospvDigitStr)
{
/* The return value: */
int retVal = OSPC_ERR_NO_ERROR;
/* The string index: */
int strIndex = 0;
/* The length of the string that is input: */
int digitStrLen = 0;
OSPM_DBGENTER(("ENTER: OSPPValidateDigitString\n"));
/* Check to make sure that the string is non-null: */
if (ospvDigitStr == OSPC_OSNULL) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The string to be checked was null.\n");
}
/* Check to make sure that the string is non-empty: */
if (retVal == OSPC_ERR_NO_ERROR) {
digitStrLen = OSPM_STRLEN((const char *)ospvDigitStr);
if (digitStrLen <= 0) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGERRORLOG(retVal, "The string to be checked was empty.\n");
}
}
/* if ( the string isn't empty or null ) then
* for ( each character )
* if ( the character isn't a digit ) then
* - set the return value to an error code.
*/
if (retVal == OSPC_ERR_NO_ERROR) {
for (strIndex = 0;
(strIndex < digitStrLen) && (retVal == OSPC_ERR_NO_ERROR);
strIndex++) {
if (OSPM_ISDIGIT(ospvDigitStr[strIndex]) == 0) {
retVal = OSPC_ERR_ENROLL_BAD_DIGIT_STR;
OSPM_DBGMISC(("%c is not a digit", ospvDigitStr[strIndex]));
}
}
}
OSPM_DBGEXIT(("EXIT: OSPPValidateDigitString\n"));
return retVal;
}
/*
* Cleanup the communications manager: its HTTP connections, security manager,
* and the communications manager itself. The only way that this function
* will return an error is if the communications manager passed in for deletion
* is null.
*
* Input: reference to the communications manager pointer to be deleted.
*/
int OSPPEnrollCleanupCommMgr(OSPTCOMM **ospvCommMgrIn)
{
int retVal = OSPC_ERR_NO_ERROR;
OSPM_DBGENTER(("ENTER: OSPPEnrollCleanupCommMgr\n"));
if ((ospvCommMgrIn == OSPC_OSNULL) || (*ospvCommMgrIn == OSPC_OSNULL)) {
retVal = OSPC_ERR_ENROLL_INVALID_ARG;
OSPM_DBGMISC(("Warning: The comm manager to be cleaned up is invalid.\n"));
}
if (retVal == OSPC_ERR_NO_ERROR) {
if ((*ospvCommMgrIn)->Security != OSPC_OSNULL) {
OSPPSSLSessionCleanup((void *)(*ospvCommMgrIn)->Security);
}
if ((*ospvCommMgrIn)->Security != OSPC_OSNULL) {
OSPPSecDelete(&((*ospvCommMgrIn)->Security));
}
OSPPCommSetShutdown(ospvCommMgrIn, 0);
}
OSPM_DBGEXIT(("EXIT: OSPPEnrollCleanupCommMgr\n"));
return retVal;
}
|