1 /*
2  * SessionRequest.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #ifndef SESSION_REQUEST_HPP
17 #define SESSION_REQUEST_HPP
18 
19 #include <shared_core/Error.hpp>
20 #include <core/system/Environment.hpp>
21 
22 #include <session/http/SessionRequest.hpp>
23 
24 #include <core/http/TcpIpBlockingClient.hpp>
25 #include <core/http/ConnectionRetryProfile.hpp>
26 #include <core/http/CSRFToken.hpp>
27 
28 #ifndef _WIN32
29 #include <core/http/LocalStreamBlockingClient.hpp>
30 #include <session/SessionLocalStreams.hpp>
31 #endif
32 
33 #include "session-config.h"
34 
35 #ifdef RSTUDIO_SERVER
36 #include <server_core/sessions/SessionSignature.hpp>
37 #endif
38 
39 #include <session/SessionConstants.hpp>
40 
41 namespace rstudio {
42 namespace session {
43 namespace http {
44 
45 // this function sends an request directly to the session; it's inlined so that
46 // it can be used both from the postback executable (which does not link with
47 // rsession) and the session itself
sendSessionRequest(const std::string & uri,const std::string & body,core::http::Response * pResponse)48 inline core::Error sendSessionRequest(const std::string& uri,
49                                       const std::string& body,
50                                       core::http::Response* pResponse)
51 {
52    // build request
53    core::http::Request request;
54    request.setMethod("POST");
55    request.setUri(uri);
56    request.setHeader("Accept", "*/*");
57    request.setHeader("Connection", "close");
58    request.setHeader("X-Session-Postback", "1");
59    request.setHeader(kRStudioUserIdentityDisplay, core::system::username());
60 
61    // generate random CSRF token for request
62    std::string token = core::system::generateUuid();
63    request.setHeader(kCSRFTokenHeader, token);
64    request.addCookie(kCSRFTokenCookie, token);
65    request.setBody(body);
66 
67    // first, attempt to send a plain old http request
68    std::string tcpipPort = core::system::getenv(kRSessionStandalonePortNumber);
69    if (tcpipPort.empty())
70    {
71       // if no standalone port, grab regular port number (desktop mode)
72       tcpipPort = core::system::getenv(kRSessionPortNumber);
73    }
74 
75    if (!tcpipPort.empty())
76    {
77       std::string sharedSecret = core::system::getenv("RS_SHARED_SECRET");
78       if (!sharedSecret.empty())
79       {
80          // if we have a shared secret to use (like in desktop mode), simply stamp it on the request
81          request.setHeader("X-Shared-Secret", sharedSecret);
82          return core::http::sendRequest("127.0.0.1", tcpipPort, request,  pResponse);
83       }
84 #ifdef RSTUDIO_SERVER
85       else
86       {
87          // otherwise, we need to authenticate via message signing with RSA crypto
88          core::Error error = server_core::sessions::signRequest(
89                   core::system::getenv("RSTUDIO_SESSION_RSA_PRIVATE_KEY"),
90                   request);
91          if (error)
92             return error;
93 
94          return core::http::sendRequest("127.0.0.1", tcpipPort, request, pResponse);
95       }
96 #endif
97    }
98 
99 #ifndef _WIN32
100    // otherwise, attempt communicating over a local stream (unix domain socket)
101    // determine stream path -- check server environment variable first
102    core::FilePath streamPath;
103    std::string stream = core::system::getenv(kRStudioSessionStream);
104    if (stream.empty())
105    {
106       // if no server environment variable, check desktop variant
107       streamPath = core::FilePath(core::system::getenv("RS_LOCAL_PEER"));
108       request.setHeader("X-Shared-Secret",
109                         core::system::getenv("RS_SHARED_SECRET"));
110    }
111    else
112    {
113       streamPath = local_streams::streamPath(stream);
114    }
115    return core::http::sendRequest(streamPath, request, pResponse);
116 #endif
117 
118    return core::Success();
119 }
120 
121 } // namespace http
122 } // namespace session
123 } // namespace rstudio
124 
125 #endif
126