xref: /petsc/src/sys/webclient/client.c (revision 0efc6a0385228cde9a68b4e67359bfe0b0532191)
1 
2 #include <petscwebclient.h>
3 
4 static BIO *bio_err = NULL;
5 
6 #define PASSWORD "password"
7 
8 #if defined(PETSC_USE_CERTIFICATE)
9 static int password_cb(char *buf,int num, int rwflag,void *userdata)
10 {
11   if (num < strlen(PASSWORD)+1) return(0);
12   strcpy(buf,PASSWORD);
13   return(strlen(PASSWORD));
14 }
15 #endif
16 
17 static void sigpipe_handle(int x)
18 {
19 }
20 
21 #undef __FUNCT__
22 #define __FUNCT__ "PetscSSLInitializeContext"
23 /*
24     PetscSSLInitializeContext - Set up an SSL context suitable for initiating HTTPS requests.
25 
26     If built with PETSC_USE_CERTIFICATE requires the user have created a self-signed certificate with
27 
28 $    ./CA.pl  -newcert  (using the passphrase of password)
29 $    cat newkey.pem newcert.pem > sslclient.pem
30 
31     and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of
32     silly but it was all I could figure out.
33 
34 */
35 PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx)
36 {
37     SSL_METHOD     *meth;
38     SSL_CTX        *ctx;
39 #if defined(PETSC_USE_CERTIFICATE)
40     char           keyfile[PETSC_MAX_PATH_LEN];
41     PetscBool      exists;
42     PetscErrorCode ierr;
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     meth = SSLv23_method();
56     ctx  = SSL_CTX_new(meth);
57 
58 #if defined(PETSC_USE_CERTIFICATE)
59     /* Locate keyfile */
60     ierr = PetscStrcpy(keyfile,"sslclient.pem");CHKERRQ(ierr);
61     ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
62     if (!exists) {
63       ierr = PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
64       ierr = PetscStrcat(keyfile,"/");CHKERRQ(ierr);
65       ierr = PetscStrcat(keyfile,"sslclient.pem");CHKERRQ(ierr);
66       ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
67       if (!exists) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate sslclient.pem file in current directory or home directory");
68     }
69 
70     /* Load our keys and certificates*/
71     if (!(SSL_CTX_use_certificate_chain_file(ctx,keyfile))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read certificate file");
72 
73     SSL_CTX_set_default_passwd_cb(ctx,password_cb);
74     if (!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read key file");
75 #endif
76 
77     *octx = ctx;
78     PetscFunctionReturn(0);
79 }
80 
81 #undef __FUNCT__
82 #define __FUNCT__ "PetscSSLDestroyContext"
83 PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx)
84 {
85   PetscFunctionBegin;
86   SSL_CTX_free(ctx);
87   PetscFunctionReturn(0);
88 }
89 
90 #undef __FUNCT__
91 #define __FUNCT__ "PetscHTTPSRequest"
92 /*
93      PetscHTTPSRequest - Send a request to an HTTPS server
94 
95    Input Parameters:
96 +   type - either "POST" or "GET"
97 .   url - complete URL of request including https://
98 .   header - additional header information, may be NULL
99 .   ctype - data type of body, for example application/json
100 .   body - data to send to server
101 .   ssl - obtained with PetscHTTPSConnect()
102 -   buffsize - size of buffer
103 
104    Output Parameter:
105 .   buff - everything returned from server
106  */
107 PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize)
108 {
109   char           *request=0;
110   char           contentlength[40],contenttype[80];
111   int            r;
112   size_t         request_len,len,headlen,bodylen,contentlen,urllen,typelen,contenttypelen = 0;
113   PetscErrorCode ierr;
114   PetscBool      flg;
115 
116   PetscFunctionBegin;
117   ierr = PetscStrbeginswith(url,"https://",&flg);CHKERRQ(ierr);
118   if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"URL must begin with https://");
119   if (header) {
120     ierr = PetscStrendswith(header,"\r\n",&flg);CHKERRQ(ierr);
121     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"header must end with \\r\\n");
122   }
123 
124   ierr = PetscStrlen(type,&typelen);CHKERRQ(ierr);
125   ierr = PetscStrlen(url,&urllen);CHKERRQ(ierr);
126   if (ctype) {
127     ierr = PetscSNPrintf(contenttype,80,"Content-Type: %s\r\n",ctype);CHKERRQ(ierr);
128     ierr = PetscStrlen(contenttype,&contenttypelen);CHKERRQ(ierr);
129   }
130   ierr = PetscStrlen(header,&headlen);CHKERRQ(ierr);
131   ierr = PetscStrlen(body,&bodylen);CHKERRQ(ierr);
132   ierr = PetscSNPrintf(contentlength,40,"Content-Length: %d\r\n\r\n",(int)bodylen);CHKERRQ(ierr);
133   ierr = PetscStrlen(contentlength,&contentlen);CHKERRQ(ierr);
134 
135   /* Now construct our HTTP request */
136   request_len = typelen + 1 + urllen + 35 + headlen + contenttypelen + contentlen + bodylen + 1;
137   ierr = PetscMalloc1(request_len,&request);CHKERRQ(ierr);
138   ierr = PetscStrcpy(request,type);CHKERRQ(ierr);
139   ierr = PetscStrcat(request," ");CHKERRQ(ierr);
140   ierr = PetscStrcat(request,url);CHKERRQ(ierr);
141   ierr = PetscStrcat(request," HTTP/1.1\r\nUser-Agent:PETScClient\r\n");CHKERRQ(ierr);
142   ierr = PetscStrcat(request,header);CHKERRQ(ierr);
143   if (ctype) {
144     ierr = PetscStrcat(request,contenttype);CHKERRQ(ierr);
145   }
146   ierr = PetscStrcat(request,contentlength);CHKERRQ(ierr);
147   ierr = PetscStrcat(request,body);CHKERRQ(ierr);
148   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
149   ierr = PetscInfo1(NULL,"HTTPS request follows: \n%s\n",request);CHKERRQ(ierr);
150 
151   r = SSL_write(ssl,request,request_len);
152   switch (SSL_get_error(ssl,r)){
153     case SSL_ERROR_NONE:
154       if (request_len != r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket");
155       break;
156       default:
157         SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem");
158   }
159 
160   /* Now read the server's response, assuming  that it's terminated by a close */
161   r = SSL_read(ssl,buff,(int)buffsize);
162   len = r;
163   switch (SSL_get_error(ssl,r)){
164   case SSL_ERROR_NONE:
165     break;
166   case SSL_ERROR_ZERO_RETURN:
167     SSL_shutdown(ssl);  /* ignore shutdown error message */
168     break;
169   case SSL_ERROR_SYSCALL:
170     break;
171   default:
172     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
173   }
174   buff[len] = 0; /* null terminate string */
175   ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr);
176 
177   SSL_free(ssl);
178   ierr = PetscFree(request);CHKERRQ(ierr);
179   PetscFunctionReturn(0);
180 }
181 
182 #undef __FUNCT__
183 #define __FUNCT__ "PetscHTTPSConnect"
184 PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl)
185 {
186   BIO            *sbio;
187   PetscErrorCode ierr;
188 
189   PetscFunctionBegin;
190   /* Connect the TCP socket*/
191   ierr = PetscOpenSocket(host,port,sock);CHKERRQ(ierr);
192 
193   /* Connect the SSL socket */
194   *ssl = SSL_new(ctx);
195   sbio = BIO_new_socket(*sock,BIO_NOCLOSE);
196   SSL_set_bio(*ssl,sbio,sbio);
197   if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error");
198   PetscFunctionReturn(0);
199 }
200 
201