diff --git a/lwadtool/include/adtool/types.h b/lwadtool/include/adtool/types.h index cb8aac3ed..7f1847dbe 100644 --- a/lwadtool/include/adtool/types.h +++ b/lwadtool/include/adtool/types.h @@ -88,7 +88,8 @@ typedef enum AdtResetComputerPasswordAction, AdtAddToGroupAction, AdtRemoveFromGroupAction, - AdtUnlockAccountAction + AdtUnlockAccountAction, + AdtSetAttrAction } AdtActionCode; /** @@ -382,6 +383,15 @@ typedef struct AdtActionUnlockAccount { PSTR computer; /* Computer name (DN/RDN, UPN, or SamAccountName) */ } AdtActionUnlockAccountT, *AdtActionUnlockAccountTP; +typedef struct AdtActionSetAttr { + AdtActionCode actionCode; /* Action code */ + VOID *opaque; /* Private data */ + PSTR dn; /* DN/RDN, UPN, or SamAccountName */ + PSTR attrName; /* Attribute name. */ + PSTR attrValue; /* Attribute value */ +} AdtActionSetAttrT, *AdtActionSetAttrTP; + + /** * Action definition. TODO: Add LW Open actions. */ @@ -419,6 +429,7 @@ typedef union AdtAction { AdtActionAddToGroupT addToGroup; AdtActionRemoveFromGroupT removeFromGroup; AdtActionUnlockAccountT unlockAccount; + AdtActionSetAttrT setAttribute; } AdtActionT, *AdtActionTP, **AdtActionTPP; typedef enum AdtOutputMode { diff --git a/lwadtool/libadtool/MakeKitBuild b/lwadtool/libadtool/MakeKitBuild index fedcc1511..af7bbb981 100644 --- a/lwadtool/libadtool/MakeKitBuild +++ b/lwadtool/libadtool/MakeKitBuild @@ -23,7 +23,8 @@ make() ldap_ops.c \ action_base.c \ ids.c \ - net.c" + net.c \ + set.c" mk_library \ LIB=adtool \ diff --git a/lwadtool/libadtool/api.c b/lwadtool/libadtool/api.c index c7ad64003..467571e77 100644 --- a/lwadtool/libadtool/api.c +++ b/lwadtool/libadtool/api.c @@ -265,6 +265,13 @@ static DWORD SetUpAction(IN AdtActionTP action) { appContext->actionCleanUpMethod = &CleanUpAdtUnlockAccountAction; break; + case (AdtSetAttrAction): + appContext->actionInitMethod = &InitAdtSetAttrAction; + appContext->actionValidateMethod = &ValidateAdtSetAttrAction; + appContext->actionExecuteMethod = &ExecuteAdtSetAttrAction; + appContext->actionCleanUpMethod = &CleanUpAdtSetAttrAction; + break; + default: break; } @@ -922,6 +929,9 @@ PCSTR AdtGetActionName(IN AdtActionCode code) { case (AdtUnlockAccountAction): return ADT_UNLOCK_ACCOUNT_ACT; + case (AdtSetAttrAction): + return ADT_SET_ATTR_ACT; + default: break; } @@ -1082,6 +1092,10 @@ AdtActionCode AdtGetActionCode(IN PSTR name) { return AdtUnlockAccountAction; } + if(!strcmp(name, ADT_SET_ATTR_ACT)) { + return AdtSetAttrAction; + } + return AdtBaseAction; } diff --git a/lwadtool/libadtool/app.h b/lwadtool/libadtool/app.h index cd3d1649f..b92133fd2 100644 --- a/lwadtool/libadtool/app.h +++ b/lwadtool/libadtool/app.h @@ -87,6 +87,7 @@ #define ADT_ADD_TO_GROUP_ACT "add-to-group" #define ADT_REMOVE_FROM_GROUP_ACT "remove-from-group" #define ADT_UNLOCK_ACCOUNT_ACT "unlock-account" +#define ADT_SET_ATTR_ACT "set-attr" #define A_DESC_HEADER "Description: " #define A_EXAMPLE_HEADER "\n\nExample: " @@ -472,6 +473,8 @@ extern DWORD InitAdtResetComputerPasswordAction(IN AdtActionTP action); extern DWORD InitAdtAddToGroupAction(IN AdtActionTP action); extern DWORD InitAdtRemoveFromGroupAction(IN AdtActionTP action); extern DWORD InitAdtUnlockAccountAction(IN AdtActionTP action); +extern DWORD InitAdtSetAttrAction(IN AdtActionTP action); + /** * Actions' validate methods. @@ -511,6 +514,8 @@ extern DWORD ValidateAdtResetComputerPasswordAction(IN AdtActionTP action); extern DWORD ValidateAdtAddToGroupAction(IN AdtActionTP action); extern DWORD ValidateAdtRemoveFromGroupAction(IN AdtActionTP action); extern DWORD ValidateAdtUnlockAccountAction(IN AdtActionTP action); +extern DWORD ValidateAdtSetAttrAction(IN AdtActionTP action); + /** * Actions' execute method. @@ -550,6 +555,8 @@ extern DWORD ExecuteAdtResetComputerPasswordAction(IN AdtActionTP action); extern DWORD ExecuteAdtAddToGroupAction(IN AdtActionTP action); extern DWORD ExecuteAdtRemoveFromGroupAction(IN AdtActionTP action); extern DWORD ExecuteAdtUnlockAccountAction(IN AdtActionTP action); +extern DWORD ExecuteAdtSetAttrAction(IN AdtActionTP action); + /** * Actions' clean up methods. @@ -591,6 +598,8 @@ extern DWORD CleanUpAdtResetComputerPasswordAction(IN AdtActionTP action); extern DWORD CleanUpAdtAddToGroupAction(IN AdtActionTP action); extern DWORD CleanUpAdtRemoveFromGroupAction(IN AdtActionTP action); extern DWORD CleanUpAdtUnlockAccountAction(IN AdtActionTP action); +extern DWORD CleanUpAdtSetAttrAction(IN AdtActionTP action); + /** * Error message buffer. diff --git a/lwadtool/libadtool/cli.c b/lwadtool/libadtool/cli.c index cd460a042..72555b94b 100644 --- a/lwadtool/libadtool/cli.c +++ b/lwadtool/libadtool/cli.c @@ -1128,11 +1128,44 @@ DWORD MakeFullArgsTable(IN AppContextTP appContext, IN struct poptOption *acts) NULL); MakeOption(ADT_TABLEEND(&((*unlockAccountAction)[i++]))); + /** + ** Set attribute options. + **/ + i = 0; + struct poptOption **setAttrAction = MakeOptions(4); + ADT_BAIL_ON_ALLOC_FAILURE(setAttrAction); + MakeOption(&((*setAttrAction)[i++]), + "dn", + '\0', + POPT_ARG_STRING, + &(appContext->action.setAttribute.dn), + 0, + "DN.[X]", + NULL); + MakeOption(&((*setAttrAction)[i++]), + "attrName", + '\0', + POPT_ARG_STRING, + &(appContext->action.setAttribute.attrName), + 0, + "Name of attribute.[X]", + NULL); + + MakeOption(&((*setAttrAction)[i++]), + "attrValue", + '\0', + POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, + &(appContext->action.setAttribute.attrValue), + 0, + "Value of attribute. Multi-value attributes are delimited with a semi-colon. To unset an attribute do not provide the attrValue argument.", + NULL); + MakeOption(ADT_TABLEEND(&((*setAttrAction)[i++]))); + /** * Open Edition options. */ i = 0; - struct poptOption **openActionsTable = MakeOptions(19); + struct poptOption **openActionsTable = MakeOptions(20); ADT_BAIL_ON_ALLOC_FAILURE(openActionsTable); MakeOption(&((*openActionsTable)[i++]), NULL, @@ -1278,6 +1311,16 @@ DWORD MakeFullArgsTable(IN AppContextTP appContext, IN struct poptOption *acts) AdtSearchUserAction, ADT_SEARCH_USER_ACT " - search for users, print DNs.", NULL); + + MakeOption(&((*openActionsTable)[i++]), + NULL, + 'O', + POPT_ARG_INCLUDE_TABLE, + *setAttrAction, + AdtSetAttrAction, + ADT_SET_ATTR_ACT " - set/un-set attribute.", + NULL); + /* MakeOption(&((*openActionsTable)[i++]), NULL, @@ -1349,6 +1392,7 @@ DWORD MakeFullArgsTable(IN AppContextTP appContext, IN struct poptOption *acts) LW_SAFE_FREE_MEMORY(addToGroupAction); LW_SAFE_FREE_MEMORY(removeFromGroupAction); LW_SAFE_FREE_MEMORY(unlockAccountAction); + LW_SAFE_FREE_MEMORY(setAttrAction); /* LW_SAFE_FREE_MEMORY(enableComputerAction); LW_SAFE_FREE_MEMORY(disableComputerAction); @@ -1618,6 +1662,18 @@ VOID PrintExamples() { "Delete the default PowerBroker Cell (assuming root naming context is DC=country,DC=company,DC=com):" NL_STR2 "adtool -a delete-cell --dn DC=country,DC=company,DC=com --force" + NL_STR + "Modify an attribute. Note: Attribute value validation is not done. Use with care:" + NL_STR2 + "adtool -a set-attr --dn CN=TestUser,CN=Users,DC=company,DC=com --attrName displayName --attrValue \"Test User\"" + NL_STR + "Modify a multi-value attribute using a semi-colon as the delimiter. Note: Attribute value validation is not done. Use with care:" + NL_STR2 + "adtool -a set-attr --dn CN=group,OU=department,DC=company,DC=com --attrName member --attrValue \"CN=Test User,OU=department,DC=company,DC=com;CN=Test User2,OU=department,DC=company,DC=com\"" + NL_STR + "Unset an attribute. Note: Attribute value validation is not done. Use with care:" + NL_STR2 + "adtool -a set-attr --dn CN=TestUser,CN=Users,DC=company,DC=com --attrName displayName" ; fprintf(stdout, "%s\n", s); diff --git a/lwadtool/libadtool/defs.h b/lwadtool/libadtool/defs.h index 5c547c9d6..96c499959 100644 --- a/lwadtool/libadtool/defs.h +++ b/lwadtool/libadtool/defs.h @@ -57,6 +57,8 @@ #define LDAP_ATTRS_MAX 5000 #define SID_CHARS_MAX 128 +#define ADT_LIST_DELIMITER ";" /* multi-list attribute separator */ + #define ADT_SAFE_LOG_STR(s) ((s)?(s):"(null)") #define ADT_IS_NULL_OR_EMPTY_STR(str) (!(str) || !(*(str))) diff --git a/lwadtool/libadtool/set.c b/lwadtool/libadtool/set.c new file mode 100644 index 000000000..4d4acca08 --- /dev/null +++ b/lwadtool/libadtool/set.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) BeyondTrust Software. All rights Reserved. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the license, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy + * of the GNU Lesser General Public License along with this program. If + * not, see . + * + * BEYONDTRUST SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING + * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT + * WITH BEYONDTRUST SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE + * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU + * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU + * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING + * TERMS OFFERED BY BEYONDTRUST SOFTWARE, PLEASE CONTACT BEYONDTRUST SOFTWARE AT + * license@beyondtrust.com + */ + +/* + * Module Name: + * + * set.c + * + * Abstract: + * + * Methods for setting attributes of directory objects. + * + * Authors: Author: CORP\rali + * + * Created on: Sep 6, 2016 + * + */ + + +#include "includes.h" + + +DWORD InitAdtSetAttrAction(IN AdtActionTP action) +{ + return InitBaseAction(action); +} + +DWORD ValidateAdtSetAttrAction(IN AdtActionTP action) +{ + DWORD dwError = 0; + AppContextTP appContext = (AppContextTP) ((AdtActionBaseTP) action)->opaque; + PSTR dn = NULL; + + dwError = OpenADSearchConnectionDN(action, &(action->setAttribute.dn)); + ADT_BAIL_ON_ERROR_NP(dwError); + + SwitchToSearchConnection(action); + + if (!action->setAttribute.dn) { + dwError = ADT_ERR_ARG_MISSING_DN; + ADT_BAIL_ON_ERROR_NP(dwError); + } + + if (!action->setAttribute.attrName) + { + dwError = ADT_ERR_ARG_MISSING_NAME; + ADT_BAIL_ON_ERROR_NP(dwError); + } + + dwError = ProcessDash(&(action->setAttribute.dn)); + ADT_BAIL_ON_ERROR_NP(dwError); + + dwError = ResolveDN(appContext, ObjectClassAny, action->setAttribute.dn, &dn); + ADT_BAIL_ON_ERROR_NP(dwError); + LW_SAFE_FREE_MEMORY(action->setAttribute.dn); + action->setAttribute.dn = dn; + + cleanup: + return dwError; + + error: + LW_SAFE_FREE_MEMORY(dn); + goto cleanup; +} + +DWORD ExecuteAdtSetAttrAction(IN AdtActionTP action) +{ + DWORD dwError = 0; + DWORD i, j = 0; + DWORD dwMaxValues = 100; + AttrValsT *avp = NULL; + AppContextTP appContext = (AppContextTP) ((AdtActionBaseTP) action)->opaque; + PSTR aStr = NULL; + PSTR saveStrPtr = NULL; + PSTR tmpStr = NULL; + + dwError = LwAllocateMemory(2 * sizeof(AttrValsT), OUT_PPVOID(&avp)); + ADT_BAIL_ON_ALLOC_FAILURE(!dwError); + + avp[0].attr = action->setAttribute.attrName; + + dwError = LwAllocateMemory(dwMaxValues * sizeof(PSTR), OUT_PPVOID(&(avp[0].vals))); + ADT_BAIL_ON_ALLOC_FAILURE(!dwError); + + for (i = 0; i < dwMaxValues; i++) + avp[0].vals[i] = NULL; + + if (action->setAttribute.attrValue) + { + dwError = LwStrDupOrNull(action->setAttribute.attrValue, &tmpStr); + ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); + + // Use semi-colon to delimit a multi-value attribute. + aStr = strtok_r(tmpStr, ADT_LIST_DELIMITER, &saveStrPtr); + i = 0; + while ((aStr != NULL) && (i < dwMaxValues)) + { + dwError = LwStrDupOrNull((PCSTR) aStr, &(avp[0].vals[i])); + ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); + aStr = strtok_r(NULL, ADT_LIST_DELIMITER, &saveStrPtr); + i++; + } + } + + dwError = ModifyADObject(appContext, action->setAttribute.dn, avp, 2); + ADT_BAIL_ON_ERROR_NP(dwError); + + if (action->setAttribute.attrValue) + PrintResult(appContext, LogLevelNone, "Successfully updated \"%s\" with: %s\n", + action->setAttribute.attrName, + action->setAttribute.attrValue); + else + PrintResult(appContext, LogLevelNone, "Cleared \"%s\" attribute\n", action->setAttribute.attrName); + +cleanup: + + if (avp) + { + for (i = 0; avp[i].vals; ++i) + { + for (j = 0; avp[i].vals[j]; ++j) + { + LW_SAFE_FREE_MEMORY(avp[i].vals[j]); + } + + LW_SAFE_FREE_MEMORY(avp[i].vals); + } + + LW_SAFE_FREE_MEMORY(avp); + } + + if (tmpStr) + LW_SAFE_FREE_STRING(tmpStr); + + return dwError; + +error: + goto cleanup; +} + +DWORD CleanUpAdtSetAttrAction(IN AdtActionTP action) +{ + return CleanUpBaseAction(action); +} + +