Skip to content

Commit fa53f87

Browse files
committed
[http] introduce fAllowPostObject flag
It allows to deserialize post data as ROOT object when processing exe.json request. While this can leads to arbitrary code loading and injection, disable this feature by default. Can be enabled back with: ``` serv->SetAllowPostObject(kTRUE); ```
1 parent 5b99c6e commit fa53f87

4 files changed

Lines changed: 39 additions & 3 deletions

File tree

net/http/inc/THttpServer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class THttpServer : public TNamed {
9494

9595
void SetReadOnly(Bool_t readonly = kTRUE);
9696

97+
Bool_t IsAllowPostObject() const;
98+
99+
void SetAllowPostObject(Bool_t allow_post_obj);
100+
97101
Bool_t IsWSOnly() const;
98102

99103
void SetWSOnly(Bool_t on = kTRUE);

net/http/inc/TRootSniffer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class TRootSniffer : public TNamed {
120120
protected:
121121
TString fObjectsPath; ///<! default path for registered objects
122122
Bool_t fReadOnly{kTRUE}; ///<! indicate if sniffer allowed to change ROOT structures - like read objects from file
123+
Bool_t fAllowPostObject{kFALSE}; ///<! when true allow to deserialize objects received via POST requests
123124
Bool_t fScanGlobalDir{kTRUE}; ///<! when enabled (default), scan gROOT for histograms, canvases, open files
124125
std::unique_ptr<TFolder> fTopFolder; ///<! own top TFolder object, used for registering objects
125126
THttpCallArg *fCurrentArg{nullptr}; ///<! current http arguments (if any)
@@ -192,6 +193,12 @@ class TRootSniffer : public TNamed {
192193
/** Returns readonly mode */
193194
Bool_t IsReadOnly() const { return fReadOnly; }
194195

196+
/** Allow to deserialize object in POST requests, default off */
197+
void SetAllowPostObject(Bool_t allow_post_obj) { fAllowPostObject = allow_post_obj; }
198+
199+
/** Is allowed to deserialize object in POST requests, default off */
200+
Bool_t IsAllowPostObject() const { return fAllowPostObject; }
201+
195202
void Restrict(const char *path, const char *options);
196203

197204
Bool_t HasRestriction(const char *item_name);

net/http/src/THttpServer.cxx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,31 @@ void THttpServer::SetReadOnly(Bool_t readonly)
275275
fSniffer->SetReadOnly(readonly);
276276
}
277277

278+
////////////////////////////////////////////////////////////////////////////////
279+
/// Returns true if server accept object content in POST reequests
280+
281+
Bool_t THttpServer::IsAllowPostObject() const
282+
{
283+
return fSniffer ? fSniffer->IsAllowPostObject() : kFALSE;
284+
}
285+
286+
////////////////////////////////////////////////////////////////////////////////
287+
/// Set flag to allow receive and desereilize objects in POST requests
288+
///
289+
/// When object methods are executed via exe.json request,
290+
/// one can supply object as binary/json/xml in the body of POST request
291+
/// To allow creation of such object, one need to enable this flag
292+
/// Use of exe.json only possible in not-readonly mode
293+
///
294+
/// CAUTION! This is sensitive functionality and therefore should be
295+
/// used only when server not exposed to publicaly-accessed netowork.
296+
297+
void THttpServer::SetAllowPostObject(Bool_t allow_post_obj)
298+
{
299+
if (fSniffer)
300+
fSniffer->SetAllowPostObject(allow_post_obj);
301+
}
302+
278303
////////////////////////////////////////////////////////////////////////////////
279304
/// returns true if only websockets are handled by the server
280305
///

net/httpsniff/src/TRootSnifferFull.cxx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
643643
sval.Form("(%s*)0x%zx", obj_cl->GetName(), (size_t)obj_ptr);
644644
} else if ((fCurrentArg != nullptr) && (fCurrentArg->GetPostData() != nullptr)) {
645645
// process several arguments which are specific for post requests
646-
if (sval == "_post_object_xml_") {
646+
if (fAllowPostObject && (sval == "_post_object_xml_")) {
647647
// post data has extra 0 at the end and can be used as null-terminated string
648648
post_obj = TBufferXML::ConvertFromXML((const char *)fCurrentArg->GetPostData());
649649
if (!post_obj)
@@ -653,7 +653,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
653653
if (url.HasOption("_destroy_post_"))
654654
garbage.Add(post_obj);
655655
}
656-
} else if (sval == "_post_object_json_") {
656+
} else if (fAllowPostObject && (sval == "_post_object_json_")) {
657657
// post data has extra 0 at the end and can be used as null-terminated string
658658
post_obj = TBufferJSON::ConvertFromJSON((const char *)fCurrentArg->GetPostData());
659659
if (!post_obj)
@@ -663,7 +663,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
663663
if (url.HasOption("_destroy_post_"))
664664
garbage.Add(post_obj);
665665
}
666-
} else if ((sval == "_post_object_") && url.HasOption("_post_class_")) {
666+
} else if (fAllowPostObject && (sval == "_post_object_") && url.HasOption("_post_class_")) {
667667
TString clname = DecodeUrlOptionValue(url.GetValueFromOptions("_post_class_"), kTRUE);
668668
TClass *arg_cl = gROOT->GetClass(clname, kTRUE, kTRUE);
669669
if ((arg_cl != nullptr) && (arg_cl->GetBaseClassOffset(TObject::Class()) == 0) && (post_obj == nullptr)) {

0 commit comments

Comments
 (0)