Skip to content

Commit 024ea45

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 099943f commit 024ea45

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
@@ -306,6 +306,31 @@ void THttpServer::SetReadOnly(Bool_t readonly)
306306
fSniffer->SetReadOnly(readonly);
307307
}
308308

309+
////////////////////////////////////////////////////////////////////////////////
310+
/// Returns true if server accept object content in POST reequests
311+
312+
Bool_t THttpServer::IsAllowPostObject() const
313+
{
314+
return fSniffer ? fSniffer->IsAllowPostObject() : kFALSE;
315+
}
316+
317+
////////////////////////////////////////////////////////////////////////////////
318+
/// Set flag to allow receive and desereilize objects in POST requests
319+
///
320+
/// When object methods are executed via exe.json request,
321+
/// one can supply object as binary/json/xml in the body of POST request
322+
/// To allow creation of such object, one need to enable this flag
323+
/// Use of exe.json only possible in not-readonly mode
324+
///
325+
/// CAUTION! This is sensitive functionality and therefore should be
326+
/// used only when server not exposed to publicaly-accessed netowork.
327+
328+
void THttpServer::SetAllowPostObject(Bool_t allow_post_obj)
329+
{
330+
if (fSniffer)
331+
fSniffer->SetAllowPostObject(allow_post_obj);
332+
}
333+
309334
////////////////////////////////////////////////////////////////////////////////
310335
/// returns true if only websockets are handled by the server
311336
///

net/httpsniff/src/TRootSnifferFull.cxx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
656656
sval.Form("(%s*)0x%zx", obj_cl->GetName(), (size_t)obj_ptr);
657657
} else if ((fCurrentArg != nullptr) && (fCurrentArg->GetPostData() != nullptr)) {
658658
// process several arguments which are specific for post requests
659-
if (sval == "_post_object_xml_") {
659+
if (fAllowPostObject && (sval == "_post_object_xml_")) {
660660
// post data has extra 0 at the end and can be used as null-terminated string
661661
post_obj = TBufferXML::ConvertFromXML((const char *)fCurrentArg->GetPostData());
662662
if (!post_obj)
@@ -666,7 +666,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
666666
if (url.HasOption("_destroy_post_"))
667667
garbage.Add(post_obj);
668668
}
669-
} else if (sval == "_post_object_json_") {
669+
} else if (fAllowPostObject && (sval == "_post_object_json_")) {
670670
// post data has extra 0 at the end and can be used as null-terminated string
671671
post_obj = TBufferJSON::ConvertFromJSON((const char *)fCurrentArg->GetPostData());
672672
if (!post_obj)
@@ -676,7 +676,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
676676
if (url.HasOption("_destroy_post_"))
677677
garbage.Add(post_obj);
678678
}
679-
} else if ((sval == "_post_object_") && url.HasOption("_post_class_")) {
679+
} else if (fAllowPostObject && (sval == "_post_object_") && url.HasOption("_post_class_")) {
680680
TString clname = DecodeUrlOptionValue(url.GetValueFromOptions("_post_class_"), kTRUE);
681681
TClass *arg_cl = gROOT->GetClass(clname, kTRUE, kTRUE);
682682
if ((arg_cl != nullptr) && (arg_cl->GetBaseClassOffset(TObject::Class()) == 0) && (post_obj == nullptr)) {

0 commit comments

Comments
 (0)