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 165 /*@C 166 PetscHTTPSRequest - Send a request to an HTTPS server 167 168 Input Parameters: 169 + type - either "POST" or "GET" 170 . url - URL of request host/path 171 . header - additional header information, may be NULL 172 . ctype - data type of body, for example application/json 173 . body - data to send to server 174 . ssl - obtained with PetscHTTPSConnect() 175 - buffsize - size of buffer 176 177 Output Parameter: 178 . buff - everything returned from server 179 180 Level: advanced 181 182 .seealso: PetscHTTPRequest(), PetscHTTPSConnect(), PetscSSLInitializeContext(), PetscSSLDestroyContext(), PetscPullJSONValue() 183 184 @*/ 185 PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize) 186 { 187 char *request; 188 int r; 189 size_t request_len,len; 190 PetscErrorCode ierr; 191 PetscBool foundbody = PETSC_FALSE; 192 193 PetscFunctionBegin; 194 ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr); 195 ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr); 196 197 r = SSL_write(ssl,request,(int)request_len); 198 switch (SSL_get_error(ssl,r)){ 199 case SSL_ERROR_NONE: 200 if (request_len != (size_t)r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket"); 201 break; 202 default: 203 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem"); 204 } 205 206 /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */ 207 ierr = PetscArrayzero(buff,buffsize);CHKERRQ(ierr); 208 len = 0; 209 foundbody = PETSC_FALSE; 210 do { 211 char *clen; 212 int cl; 213 size_t nlen; 214 215 r = SSL_read(ssl,buff+len,(int)buffsize); 216 len += r; 217 switch (SSL_get_error(ssl,r)){ 218 case SSL_ERROR_NONE: 219 break; 220 case SSL_ERROR_ZERO_RETURN: 221 foundbody = PETSC_TRUE; 222 SSL_shutdown(ssl); 223 break; 224 case SSL_ERROR_SYSCALL: 225 foundbody = PETSC_TRUE; 226 break; 227 default: 228 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem"); 229 } 230 231 ierr = PetscStrstr(buff,"Content-Length: ",&clen);CHKERRQ(ierr); 232 if (clen) { 233 clen += 15; 234 sscanf(clen,"%d",&cl); 235 if (!cl) foundbody = PETSC_TRUE; 236 else { 237 ierr = PetscStrstr(buff,"\r\n\r\n",&clen);CHKERRQ(ierr); 238 if (clen) { 239 ierr = PetscStrlen(clen,&nlen);CHKERRQ(ierr); 240 if (nlen-4 == (size_t) cl) foundbody = PETSC_TRUE; 241 } 242 } 243 } else { 244 /* if no content length than must leave because you don't know if you can read again */ 245 foundbody = PETSC_TRUE; 246 } 247 } while (!foundbody); 248 ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr); 249 250 SSL_free(ssl); 251 ierr = PetscFree(request);CHKERRQ(ierr); 252 PetscFunctionReturn(0); 253 } 254 255 /*@C 256 PetscHTTPRequest - Send a request to an HTTP server 257 258 Input Parameters: 259 + type - either "POST" or "GET" 260 . url - URL of request host/path 261 . header - additional header information, may be NULL 262 . ctype - data type of body, for example application/json 263 . body - data to send to server 264 . sock - obtained with PetscOpenSocket() 265 - buffsize - size of buffer 266 267 Output Parameter: 268 . buff - everything returned from server 269 270 Level: advanced 271 272 .seealso: PetscHTTPSRequest(), PetscOpenSocket(), PetscHTTPSConnect(), PetscPullJSONValue() 273 @*/ 274 PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize) 275 { 276 char *request; 277 size_t request_len; 278 PetscErrorCode ierr; 279 280 PetscFunctionBegin; 281 ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr); 282 ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr); 283 284 ierr = PetscBinaryWrite(sock,request,request_len,PETSC_CHAR);CHKERRQ(ierr); 285 ierr = PetscFree(request);CHKERRQ(ierr); 286 PetscBinaryRead(sock,buff,buffsize,NULL,PETSC_CHAR); 287 buff[buffsize-1] = 0; 288 ierr = PetscInfo1(NULL,"HTTP result follows: \n%s\n",buff);CHKERRQ(ierr); 289 PetscFunctionReturn(0); 290 } 291 292 /*@C 293 PetscHTTPSConnect - connect to a HTTPS server 294 295 Input Parameters: 296 + host - the name of the machine hosting the HTTPS server 297 . port - the port number where the server is hosting, usually 443 298 - ctx - value obtained with PetscSSLInitializeContext() 299 300 Output Parameters: 301 + sock - socket to connect 302 - ssl - the argument passed to PetscHTTPSRequest() 303 304 Level: advanced 305 306 .seealso: PetscOpenSocket(), PetscHTTPSRequest(), PetscSSLInitializeContext() 307 @*/ 308 PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl) 309 { 310 BIO *sbio; 311 PetscErrorCode ierr; 312 313 PetscFunctionBegin; 314 /* Connect the TCP socket*/ 315 ierr = PetscOpenSocket(host,port,sock);CHKERRQ(ierr); 316 317 /* Connect the SSL socket */ 318 *ssl = SSL_new(ctx); 319 sbio = BIO_new_socket(*sock,BIO_NOCLOSE); 320 SSL_set_bio(*ssl,sbio,sbio); 321 if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error"); 322 PetscFunctionReturn(0); 323 } 324 325 /*@C 326 PetscPullJSONValue - Given a JSON response containing the substring with "key" : "value" where there may or not be spaces around the : returns the value. 327 328 Input Parameters: 329 + buff - the char array containing the possible values 330 . key - the key of the requested value 331 - valuelen - the length of the array to contain the value associated with the key 332 333 Output Parameters: 334 + value - the value obtained 335 - found - flag indicating if the value was found in the buff 336 337 Level: advanced 338 339 @*/ 340 PetscErrorCode PetscPullJSONValue(const char buff[],const char key[],char value[],size_t valuelen,PetscBool *found) 341 { 342 PetscErrorCode ierr; 343 char *v,*w; 344 char work[256]; 345 size_t len; 346 347 PetscFunctionBegin; 348 ierr = PetscStrcpy(work,"\"");CHKERRQ(ierr); 349 ierr = PetscStrlcat(work,key,sizeof(work));CHKERRQ(ierr); 350 ierr = PetscStrcat(work,"\":");CHKERRQ(ierr); 351 ierr = PetscStrstr(buff,work,&v);CHKERRQ(ierr); 352 ierr = PetscStrlen(work,&len);CHKERRQ(ierr); 353 if (v) { 354 v += len; 355 } else { 356 work[len++-1] = 0; 357 ierr = PetscStrcat(work," :");CHKERRQ(ierr); 358 ierr = PetscStrstr(buff,work,&v);CHKERRQ(ierr); 359 if (!v) { 360 *found = PETSC_FALSE; 361 PetscFunctionReturn(0); 362 } 363 v += len; 364 } 365 ierr = PetscStrchr(v,'\"',&v);CHKERRQ(ierr); 366 if (!v) { 367 *found = PETSC_FALSE; 368 PetscFunctionReturn(0); 369 } 370 ierr = PetscStrchr(v+1,'\"',&w);CHKERRQ(ierr); 371 if (!w) { 372 *found = PETSC_FALSE; 373 PetscFunctionReturn(0); 374 } 375 *found = PETSC_TRUE; 376 ierr = PetscStrncpy(value,v+1,PetscMin((size_t)(w-v),valuelen));CHKERRQ(ierr); 377 PetscFunctionReturn(0); 378 } 379 380 #include <ctype.h> 381 382 /*@C 383 PetscPushJSONValue - Puts a "key" : "value" pair onto a string 384 385 Input Parameters: 386 + buffer - the char array where the value will be put 387 . key - the key value to be set 388 . value - the value associated with the key 389 - bufflen - the size of the buffer (currently ignored) 390 391 Level: advanced 392 393 Notes: 394 Ignores lengths so can cause buffer overflow 395 @*/ 396 PetscErrorCode PetscPushJSONValue(char buff[],const char key[],const char value[],size_t bufflen) 397 { 398 PetscErrorCode ierr; 399 size_t len; 400 PetscBool special; 401 402 PetscFunctionBegin; 403 ierr = PetscStrcmp(value,"null",&special);CHKERRQ(ierr); 404 if (!special) { 405 ierr = PetscStrcmp(value,"true",&special);CHKERRQ(ierr); 406 } 407 if (!special) { 408 ierr = PetscStrcmp(value,"false",&special);CHKERRQ(ierr); 409 } 410 if (!special) { 411 PetscInt i; 412 413 ierr = PetscStrlen(value,&len);CHKERRQ(ierr); 414 special = PETSC_TRUE; 415 for (i=0; i<(int)len; i++) { 416 if (!isdigit(value[i])) { 417 special = PETSC_FALSE; 418 break; 419 } 420 } 421 } 422 423 ierr = PetscStrcat(buff,"\"");CHKERRQ(ierr); 424 ierr = PetscStrcat(buff,key);CHKERRQ(ierr); 425 ierr = PetscStrcat(buff,"\":");CHKERRQ(ierr); 426 if (!special) { 427 ierr = PetscStrcat(buff,"\"");CHKERRQ(ierr); 428 } 429 ierr = PetscStrcat(buff,value);CHKERRQ(ierr); 430 if (!special) { 431 ierr = PetscStrcat(buff,"\"");CHKERRQ(ierr); 432 } 433 PetscFunctionReturn(0); 434 } 435