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