4.1.28.3 Server Behavior of the IDL_DRSWriteSPN Method

Informative summary of behavior: The IDL_DRSWriteSPN method updates the servicePrincipalName attribute of an object. The values of this multivalued attribute are called service principal names (SPNs). The IDL_DRSWriteSPN method does one of three things:

  • Adds a non-empty set of SPNs to the object's servicePrincipalName. If a member of the set is already present on the object's servicePrincipalName, it is ignored.

  • Removes all current values from the object's servicePrincipalName, then adds a (possibly empty) set of SPNs to the object's servicePrincipalName.

  • Removes a non-empty set of SPNs from the object's servicePrincipalName. If a member of the set is not present on the object's servicePrincipalName, it is ignored.

The effect of this method can be achieved by an LDAP Modify operation to the servicePrincipalName attribute of an object. Some manipulations of the servicePrincipalName attribute that cannot be performed using this method can be performed using LDAP Modify. For example, an LDAP Modify can remove one specific SPN from the servicePrincipalName attribute while adding another SPN to the servicePrincipalName attribute in the same transaction; IDL_DRSWriteSPN cannot do this.

 ULONG
 IDL_DRSWriteSPN(
     [in, ref] DRS_HANDLE hDrs,
     [in] DWORD dwInVersion,
     [in, ref, switch_is(dwInVersion)]
         DRS_MSG_SPNREQ *pmsgIn,
     [out, ref] DWORD *pdwOutVersion,
     [out, ref, switch_is(*pdwOutVersion)]
         DRS_MSG_SPNREPLY *pmsgOut);
  
 accountDN: unicodestring
 account: DSName
 err: DWORD
 operation: DS_SPN_OPERATION
 cSPN: integer
 spnSet: set of unicodestring
 instanceName: unicodestring
  
 ValidateDRSInput(hDrs, 13)
  
 pdwOutVersion^ := 1
 pmsgOut^.V1.retVal := 0
  
 /* Input parameter validation */
 if dwInVersion ≠ 1 then
   pmsgOut^.V1.retVal := ERROR_INVALID_PARAMETER
   return ERROR_INVALID_PARAMETER
 endif
  
 /* Input parameter validation */
 if ClientUUID(hDrs) ≠ NTDSAPI_CLIENT_GUID
   pmsgOut^.V1.retVal := ERROR_INVALID_PARAMETER
   return ERROR_INVALID_PARAMETER
 endif
  
 /* RODCs do not perform originating updates */
 if AmIRODC() then
   return ExecuteWriteSPNRemotely(dwInVersion,
              pmsgIn, pdwOutVersion, pmsgOut);
 endif
  
 accountDN := pmsgIn^.V1.pwszAccount
 operation := pmsgIn^.V1.operation
 cSPN := pmsgIn^.V1.cSPN
 spnSet := pmsgIn^.V1.rpwszSPN
  
 if accountDN = null or accountDN = "" then
   pmsgOut^.V1.retVal := ERROR_INVALID_PARAMETER
   return ERROR_INVALID_PARAMETER
 endif
  
 if not operation in [DS_SPN_ADD_SPN_OP .. DS_SPN_DELETE_SPN_OP] then
   pmsgOut^.V1.retVal := ERROR_INVALID_FUNCTION
   return ERROR_INVALID_FUNCTION
 endif
  
 /* DS_SPN_REPLACE_SPN_OP permits 0 SPNs to be specified (meaning 
  * "delete all SPNs"). Other operations require >=1 SPNs to be 
  * specified. */
 if (operation ≠ DS_SPN_REPLACE_SPN_OP) and (cSPN = 0) then
   pmsgOut^.V1.retVal := ERROR_INVALID_PARAMETER
   return ERROR_INVALID_PARAMETER
 endif
  
 /* The empty string is an invalid SPN. */
 foreach spn in spnSet
   if spn = null or spn = "" then
     pmsgOut^.V1.retVal := ERROR_INVALID_PARAMETER
     return ERROR_INVALID_PARAMETER
   endif
 endfor
  
 account := GetDSNameFromDN(accountDN);
 if not ObjExists(account) then
   pmsgOut^.V1.retVal := ERROR_DS_OBJ_NOT_FOUND
   return ERROR_DS_OBJ_NOT_FOUND
 endif
  
 /* Perform access checks */
 err = AccessCheckWriteToSpnAttribute(account, spnSet)
 if err ≠ ERROR_SUCCESS then
   pmsgOut^.V1.retVal := err
   return err
 endif
  
 if (operation = DS_SPN_DELETE_SPN_OP) then
   /* Remove specified SPNs */
   foreach spn in spnSet
     if spn in account!servicePrincipalName then
       account!servicePrincipalName :=
           account!servicePrincipalName - {spn}
     endif
   endfor
   return 0
 endif
  
 if (operation = DS_SPN_ADD_SPN_OP) then
   /* Add specified SPNs */
   foreach spn in spnSet
     account!servicePrincipalName :=
         account!servicePrincipalName + {spn}
   endfor
   return 0
 endif
  
 /* Must be DS_SPN_REPLACE_SPN_OP.
  * Remove all existing SPNs, then add in the specified SPNs. */
 account!servicePrincipalName := {null}
 foreach spn in spnSet
   account!servicePrincipalName :=
       account!servicePrincipalName + {spn}
 endfor
 return 0