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