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