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