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