Skip to content

Commit 24b49ca

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 11dacb0 commit 24b49ca

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)
@@ -190,6 +191,12 @@ class TRootSniffer : public TNamed {
190191
/** Returns readonly mode */
191192
Bool_t IsReadOnly() const { return fReadOnly; }
192193

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

195202
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
@@ -298,6 +298,31 @@ void THttpServer::SetReadOnly(Bool_t readonly)
298298
fSniffer->SetReadOnly(readonly);
299299
}
300300

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

net/httpsniff/src/TRootSnifferFull.cxx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
604604
sval.Form("(%s*)0x%zx", obj_cl->GetName(), (size_t)obj_ptr);
605605
} else if ((fCurrentArg != nullptr) && (fCurrentArg->GetPostData() != nullptr)) {
606606
// process several arguments which are specific for post requests
607-
if (sval == "_post_object_xml_") {
607+
if (fAllowPostObject && (sval == "_post_object_xml_")) {
608608
// post data has extra 0 at the end and can be used as null-terminated string
609609
post_obj = TBufferXML::ConvertFromXML((const char *)fCurrentArg->GetPostData());
610610
if (!post_obj)
@@ -614,7 +614,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
614614
if (url.HasOption("_destroy_post_"))
615615
garbage.Add(post_obj);
616616
}
617-
} else if (sval == "_post_object_json_") {
617+
} else if (fAllowPostObject && (sval == "_post_object_json_")) {
618618
// post data has extra 0 at the end and can be used as null-terminated string
619619
post_obj = TBufferJSON::ConvertFromJSON((const char *)fCurrentArg->GetPostData());
620620
if (!post_obj)
@@ -624,7 +624,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string &
624624
if (url.HasOption("_destroy_post_"))
625625
garbage.Add(post_obj);
626626
}
627-
} else if ((sval == "_post_object_") && url.HasOption("_post_class_")) {
627+
} else if (fAllowPostObject && (sval == "_post_object_") && url.HasOption("_post_class_")) {
628628
TString clname = DecodeUrlOptionValue(url.GetValueFromOptions("_post_class_"), kTRUE);
629629
TClass *arg_cl = gROOT->GetClass(clname, kTRUE, kTRUE);
630630
if ((arg_cl != nullptr) && (arg_cl->GetBaseClassOffset(TObject::Class()) == 0) && (post_obj == nullptr)) {

0 commit comments

Comments
 (0)