1 2 #include <petscwebclient.h> 3 PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wdeprecated-declarations") 4 5 static BIO *bio_err = NULL; 6 7 #define PASSWORD "password" 8 9 #if defined(PETSC_USE_SSL_CERTIFICATE) 10 static int password_cb(char *buf, int num, int rwflag, void *userdata) 11 { 12 if (num < strlen(PASSWORD) + 1) return (0); 13 strcpy(buf, PASSWORD); 14 return (strlen(PASSWORD)); 15 } 16 #endif 17 18 static void sigpipe_handle(int x) { } 19 20 /*@C 21 PetscSSLInitializeContext - Set up an SSL context suitable for initiating HTTPS requests. 22 23 Output Parameter: 24 . octx - the SSL_CTX to be passed to `PetscHTTPSConnect90` 25 26 Level: advanced 27 28 If PETSc was ./configure -with-ssl-certificate requires the user have created a self-signed certificate with 29 $ saws/CA.pl -newcert (using the passphrase of password) 30 $ cat newkey.pem newcert.pem > sslclient.pem 31 32 and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of 33 silly but it was all I could figure out. 34 35 .seealso: `PetscSSLDestroyContext()`, `PetscHTTPSConnect()`, `PetscHTTPSRequest()` 36 @*/ 37 PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx) 38 { 39 SSL_CTX *ctx; 40 #if defined(PETSC_USE_SSL_CERTIFICATE) 41 char keyfile[PETSC_MAX_PATH_LEN]; 42 PetscBool exists; 43 #endif 44 45 PetscFunctionBegin; 46 if (!bio_err) { 47 SSL_library_init(); 48 SSL_load_error_strings(); 49 bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); 50 } 51 52 /* Set up a SIGPIPE handler */ 53 signal(SIGPIPE, sigpipe_handle); 54 55 /* suggested at https://mta.openssl.org/pipermail/openssl-dev/2015-May/001449.html */ 56 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) 57 ctx = SSL_CTX_new(TLS_client_method()); 58 #else 59 ctx = SSL_CTX_new(SSLv23_client_method()); 60 #endif 61 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 62 63 #if defined(PETSC_USE_SSL_CERTIFICATE) 64 /* Locate keyfile */ 65 PetscCall(PetscStrncpy(keyfile, "sslclient.pem", sizeof(keyfile))); 66 PetscCall(PetscTestFile(keyfile, 'r', &exists)); 67 if (!exists) { 68 PetscCall(PetscGetHomeDirectory(keyfile, PETSC_MAX_PATH_LEN)); 69 PetscCall(PetscStrlcat(keyfile, "/", sizeof(keyfile))); 70 PetscCall(PetscStrlcat(keyfile, "sslclient.pem", sizeof(keyfile))); 71 PetscCall(PetscTestFile(keyfile, 'r', &exists)); 72 PetscCheck(exists, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate sslclient.pem file in current directory or home directory"); 73 } 74 75 /* Load our keys and certificates*/ 76 PetscCheck(SSL_CTX_use_certificate_chain_file(ctx, keyfile), PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot read certificate file"); 77 78 SSL_CTX_set_default_passwd_cb(ctx, password_cb); 79 PetscCheck(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM), PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Cannot read key file"); 80 #endif 81 82 *octx = ctx; 83 PetscFunctionReturn(PETSC_SUCCESS); 84 } 85 86 /*@C 87 PetscSSLDestroyContext - frees a `SSL_CTX` obtained with `PetscSSLInitializeContext()` 88 89 Input Parameter: 90 . ctx - the `SSL_CTX` 91 92 Level: advanced 93 94 .seealso: `PetscSSLInitializeContext()`, `PetscHTTPSConnect()` 95 @*/ 96 PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx) 97 { 98 PetscFunctionBegin; 99 SSL_CTX_free(ctx); 100 PetscFunctionReturn(PETSC_SUCCESS); 101 } 102 103 static PetscErrorCode PetscHTTPBuildRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], char **outrequest) 104 { 105 char *request = 0; 106 char contentlength[40], contenttype[80], *path, *host; 107 size_t request_len, headlen, bodylen, contentlen, pathlen, hostlen, typelen, contenttypelen = 0; 108 PetscBool flg; 109 110 PetscFunctionBegin; 111 PetscCall(PetscStrallocpy(url, &host)); 112 PetscCall(PetscStrchr(host, '/', &path)); 113 PetscCheck(path, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "url must contain / it is %s", url); 114 *path = 0; 115 PetscCall(PetscStrlen(host, &hostlen)); 116 117 PetscCall(PetscStrchr(url, '/', &path)); 118 PetscCall(PetscStrlen(path, &pathlen)); 119 120 if (header) { 121 PetscCall(PetscStrendswith(header, "\r\n", &flg)); 122 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "header must end with \\r\\n"); 123 } 124 125 PetscCall(PetscStrlen(type, &typelen)); 126 if (ctype) { 127 PetscCall(PetscSNPrintf(contenttype, 80, "Content-Type: %s\r\n", ctype)); 128 PetscCall(PetscStrlen(contenttype, &contenttypelen)); 129 } 130 PetscCall(PetscStrlen(header, &headlen)); 131 PetscCall(PetscStrlen(body, &bodylen)); 132 PetscCall(PetscSNPrintf(contentlength, 40, "Content-Length: %d\r\n\r\n", (int)bodylen)); 133 PetscCall(PetscStrlen(contentlength, &contentlen)); 134 135 /* Now construct our HTTP request */ 136 request_len = typelen + 1 + pathlen + hostlen + 100 + headlen + contenttypelen + contentlen + bodylen + 1; 137 PetscCall(PetscMalloc1(request_len, &request)); 138 PetscCall(PetscStrncpy(request, type, request_len)); 139 PetscCall(PetscStrlcat(request, " ", request_len)); 140 PetscCall(PetscStrlcat(request, path, request_len)); 141 PetscCall(PetscStrlcat(request, " HTTP/1.1\r\nHost: ", request_len)); 142 PetscCall(PetscStrlcat(request, host, request_len)); 143 PetscCall(PetscFree(host)); 144 PetscCall(PetscStrlcat(request, "\r\nUser-Agent:PETScClient\r\n", request_len)); 145 PetscCall(PetscStrlcat(request, header, request_len)); 146 if (ctype) PetscCall(PetscStrlcat(request, contenttype, request_len)); 147 PetscCall(PetscStrlcat(request, contentlength, request_len)); 148 PetscCall(PetscStrlcat(request, body, request_len)); 149 PetscCall(PetscStrlen(request, &request_len)); 150 PetscCall(PetscInfo(NULL, "HTTPS request follows: \n%s\n", request)); 151 152 *outrequest = request; 153 PetscFunctionReturn(PETSC_SUCCESS); 154 } 155 156 /*@C 157 PetscHTTPSRequest - Send a request to an HTTPS server 158 159 Input Parameters: 160 + type - either "POST" or "GET" 161 . url - URL of request host/path 162 . header - additional header information, may be NULL 163 . ctype - data type of body, for example application/json 164 . body - data to send to server 165 . ssl - obtained with `PetscHTTPSConnect()` 166 - buffsize - size of buffer 167 168 Output Parameter: 169 . buff - everything returned from server 170 171 Level: advanced 172 173 .seealso: `PetscHTTPRequest()`, `PetscHTTPSConnect()`, `PetscSSLInitializeContext()`, `PetscSSLDestroyContext()`, `PetscPullJSONValue()` 174 @*/ 175 PetscErrorCode PetscHTTPSRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], SSL *ssl, char buff[], size_t buffsize) 176 { 177 char *request; 178 int r; 179 size_t request_len, len; 180 PetscBool foundbody = PETSC_FALSE; 181 182 PetscFunctionBegin; 183 PetscCall(PetscHTTPBuildRequest(type, url, header, ctype, body, &request)); 184 PetscCall(PetscStrlen(request, &request_len)); 185 186 r = SSL_write(ssl, request, (int)request_len); 187 switch (SSL_get_error(ssl, r)) { 188 case SSL_ERROR_NONE: 189 PetscCheck(request_len == (size_t)r, PETSC_COMM_SELF, PETSC_ERR_LIB, "Incomplete write to SSL socket"); 190 break; 191 default: 192 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL socket write problem"); 193 } 194 195 /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */ 196 PetscCall(PetscArrayzero(buff, buffsize)); 197 len = 0; 198 foundbody = PETSC_FALSE; 199 do { 200 char *clen; 201 int cl; 202 size_t nlen; 203 204 r = SSL_read(ssl, buff + len, (int)buffsize); 205 len += r; 206 switch (SSL_get_error(ssl, r)) { 207 case SSL_ERROR_NONE: 208 break; 209 case SSL_ERROR_ZERO_RETURN: 210 foundbody = PETSC_TRUE; 211 SSL_shutdown(ssl); 212 break; 213 case SSL_ERROR_SYSCALL: 214 foundbody = PETSC_TRUE; 215 break; 216 default: 217 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL read problem"); 218 } 219 220 PetscCall(PetscStrstr(buff, "Content-Length: ", &clen)); 221 if (clen) { 222 clen += 15; 223 sscanf(clen, "%d", &cl); 224 if (!cl) foundbody = PETSC_TRUE; 225 else { 226 PetscCall(PetscStrstr(buff, "\r\n\r\n", &clen)); 227 if (clen) { 228 PetscCall(PetscStrlen(clen, &nlen)); 229 if (nlen - 4 == (size_t)cl) foundbody = PETSC_TRUE; 230 } 231 } 232 } else { 233 /* if no content length than must leave because you don't know if you can read again */ 234 foundbody = PETSC_TRUE; 235 } 236 } while (!foundbody); 237 PetscCall(PetscInfo(NULL, "HTTPS result follows: \n%s\n", buff)); 238 239 SSL_free(ssl); 240 PetscCall(PetscFree(request)); 241 PetscFunctionReturn(PETSC_SUCCESS); 242 } 243 244 /*@C 245 PetscHTTPRequest - Send a request to an HTTP server 246 247 Input Parameters: 248 + type - either "POST" or "GET" 249 . url - URL of request host/path 250 . header - additional header information, may be NULL 251 . ctype - data type of body, for example application/json 252 . body - data to send to server 253 . sock - obtained with `PetscOpenSocket()` 254 - buffsize - size of buffer 255 256 Output Parameter: 257 . buff - everything returned from server 258 259 Level: advanced 260 261 .seealso: `PetscHTTPSRequest()`, `PetscOpenSocket()`, `PetscHTTPSConnect()`, `PetscPullJSONValue()` 262 @*/ 263 PetscErrorCode PetscHTTPRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], int sock, char buff[], size_t buffsize) 264 { 265 char *request; 266 size_t request_len; 267 268 PetscFunctionBegin; 269 PetscCall(PetscHTTPBuildRequest(type, url, header, ctype, body, &request)); 270 PetscCall(PetscStrlen(request, &request_len)); 271 272 PetscCall(PetscBinaryWrite(sock, request, request_len, PETSC_CHAR)); 273 PetscCall(PetscFree(request)); 274 PetscCall(PetscBinaryRead(sock, buff, buffsize, NULL, PETSC_CHAR)); 275 buff[buffsize - 1] = 0; 276 PetscCall(PetscInfo(NULL, "HTTP result follows: \n%s\n", buff)); 277 PetscFunctionReturn(PETSC_SUCCESS); 278 } 279 280 /*@C 281 PetscHTTPSConnect - connect to a HTTPS server 282 283 Input Parameters: 284 + host - the name of the machine hosting the HTTPS server 285 . port - the port number where the server is hosting, usually 443 286 - ctx - value obtained with `PetscSSLInitializeContext()` 287 288 Output Parameters: 289 + sock - socket to connect 290 - ssl - the argument passed to `PetscHTTPSRequest()` 291 292 Level: advanced 293 294 .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()` 295 @*/ 296 PetscErrorCode PetscHTTPSConnect(const char host[], int port, SSL_CTX *ctx, int *sock, SSL **ssl) 297 { 298 BIO *sbio; 299 300 PetscFunctionBegin; 301 /* Connect the TCP socket*/ 302 PetscCall(PetscOpenSocket(host, port, sock)); 303 304 /* Connect the SSL socket */ 305 *ssl = SSL_new(ctx); 306 sbio = BIO_new_socket(*sock, BIO_NOCLOSE); 307 SSL_set_bio(*ssl, sbio, sbio); 308 PetscCheck(SSL_connect(*ssl) > 0, PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL connect error"); 309 PetscFunctionReturn(PETSC_SUCCESS); 310 } 311 312 /*@C 313 PetscPullJSONValue - Given a JSON response containing the substring with "key" : "value" where there may or not be spaces around the : returns the value. 314 315 Input Parameters: 316 + buff - the char array containing the possible values 317 . key - the key of the requested value 318 - valuelen - the length of the array to contain the value associated with the key 319 320 Output Parameters: 321 + value - the value obtained 322 - found - flag indicating if the value was found in the buff 323 324 Level: advanced 325 326 .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`, `PetscPushJSONValue()` 327 @*/ 328 PetscErrorCode PetscPullJSONValue(const char buff[], const char key[], char value[], size_t valuelen, PetscBool *found) 329 { 330 char *v, *w; 331 char work[256]; 332 size_t len; 333 334 PetscFunctionBegin; 335 PetscCall(PetscStrncpy(work, "\"", sizeof(work))); 336 PetscCall(PetscStrlcat(work, key, sizeof(work))); 337 PetscCall(PetscStrlcat(work, "\":", sizeof(work))); 338 PetscCall(PetscStrstr(buff, work, &v)); 339 PetscCall(PetscStrlen(work, &len)); 340 if (v) { 341 v += len; 342 } else { 343 work[len++ - 1] = 0; 344 PetscCall(PetscStrlcat(work, " :", sizeof(work))); 345 PetscCall(PetscStrstr(buff, work, &v)); 346 if (!v) { 347 *found = PETSC_FALSE; 348 PetscFunctionReturn(PETSC_SUCCESS); 349 } 350 v += len; 351 } 352 PetscCall(PetscStrchr(v, '\"', &v)); 353 if (!v) { 354 *found = PETSC_FALSE; 355 PetscFunctionReturn(PETSC_SUCCESS); 356 } 357 PetscCall(PetscStrchr(v + 1, '\"', &w)); 358 if (!w) { 359 *found = PETSC_FALSE; 360 PetscFunctionReturn(PETSC_SUCCESS); 361 } 362 *found = PETSC_TRUE; 363 PetscCall(PetscStrncpy(value, v + 1, PetscMin((size_t)(w - v), valuelen))); 364 PetscFunctionReturn(PETSC_SUCCESS); 365 } 366 367 #include <ctype.h> 368 369 /*@C 370 PetscPushJSONValue - Puts a "key" : "value" pair onto a string 371 372 Input Parameters: 373 + buffer - the char array where the value will be put 374 . key - the key value to be set 375 . value - the value associated with the key 376 - bufflen - the size of the buffer (currently ignored) 377 378 Level: advanced 379 380 Note: 381 Ignores lengths so can cause buffer overflow 382 383 .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`, `PetscPullJSONValue()` 384 @*/ 385 PetscErrorCode PetscPushJSONValue(char buff[], const char key[], const char value[], size_t bufflen) 386 { 387 size_t len; 388 PetscBool special; 389 390 PetscFunctionBegin; 391 PetscCall(PetscStrcmp(value, "null", &special)); 392 if (!special) PetscCall(PetscStrcmp(value, "true", &special)); 393 if (!special) PetscCall(PetscStrcmp(value, "false", &special)); 394 if (!special) { 395 PetscInt i; 396 397 PetscCall(PetscStrlen(value, &len)); 398 special = PETSC_TRUE; 399 for (i = 0; i < (int)len; i++) { 400 if (!isdigit(value[i])) { 401 special = PETSC_FALSE; 402 break; 403 } 404 } 405 } 406 407 PetscCall(PetscStrlcat(buff, "\"", bufflen)); 408 PetscCall(PetscStrlcat(buff, key, bufflen)); 409 PetscCall(PetscStrlcat(buff, "\":", bufflen)); 410 if (!special) PetscCall(PetscStrlcat(buff, "\"", bufflen)); 411 PetscCall(PetscStrlcat(buff, value, bufflen)); 412 if (!special) PetscCall(PetscStrlcat(buff, "\"", bufflen)); 413 PetscFunctionReturn(PETSC_SUCCESS); 414 } 415