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