void VPathHandleFilePath(STREAM *S,HTTPSession *Session, TPathItem *VPath, int SendData) { char *Tempstr=NULL, *ptr; char *LocalPath=NULL, *ExternalPath=NULL, *DocName=NULL; //Document name here is whatever part of the Path is *beyond* the VPath component DocName=HTTPServerSubstituteArgs(DocName, Session->Path+StrLen(VPath->URL), Session); ptr=GetToken(VPath->Path,":",&Tempstr,0); while (ptr) { if (*Tempstr=='/') ExternalPath=MCatStr(ExternalPath,Tempstr,":",NULL); else { if (StrLen(Tempstr)==0) LocalPath=CatStr(LocalPath,"/:"); else LocalPath=MCatStr(LocalPath,Tempstr,":",NULL); } ptr=GetToken(ptr,":",&Tempstr,0); } Tempstr=CopyStr(Tempstr,""); if (StrLen(LocalPath)) Tempstr=FindFileInPath(Tempstr,DocName,LocalPath); if (StrLen(Tempstr)) HTTPServerSendDocument(S, Session, Tempstr, HEADERS_SENDFILE|HEADERS_USECACHE|HEADERS_KEEPALIVE); else if (StrLen(ExternalPath)) { if (strcmp(Session->Method,"POST")==0) { if (VPath->Flags & PATHITEM_UPLOAD) { LogToFile(Settings.LogPath,"%s@%s (%s) uploading to %s in VPATH %s", Session->UserName,Session->ClientHost,Session->ClientIP,DocName,ExternalPath); ChrootProcessRequest(S, Session, "POST", DocName, ExternalPath); } else { LogToFile(Settings.LogPath,"%s@%s (%s) uploading DENIED to %s in VPATH %s", Session->UserName,Session->ClientHost,Session->ClientIP,DocName,ExternalPath); HTTPServerSendHTML(S, Session, "403 Forbidden","Uploads not allowed to this path."); } } else { LogToFile(Settings.LogPath,"%s@%s (%s) asking for external document %s in Search path %s", Session->UserName,Session->ClientHost,Session->ClientIP,DocName,ExternalPath); ChrootProcessRequest(S, Session, "GETF", DocName, ExternalPath); } } //This will send '404' else HTTPServerSendDocument(S, Session, DocName, HEADERS_SENDFILE|HEADERS_USECACHE|HEADERS_KEEPALIVE); DestroyString(DocName); DestroyString(Tempstr); DestroyString(LocalPath); DestroyString(ExternalPath); }
//This is the function we call in the child process for 'SpawnCommand' int BASIC_FUNC_EXEC_COMMAND(void *Command, int Flags) { int result; char *Token=NULL, *FinalCommand=NULL, *ExecPath=NULL; char **argv; const char *ptr; int i; if (Flags & SPAWN_TRUST_COMMAND) FinalCommand=CopyStr(FinalCommand, (char *) Command); else FinalCommand=MakeShellSafeString(FinalCommand, (char *) Command, 0); StripTrailingWhitespace(FinalCommand); if (Flags & SPAWN_NOSHELL) { argv=(char **) calloc(101,sizeof(char *)); ptr=FinalCommand; ptr=GetToken(FinalCommand,"\\S",&Token,GETTOKEN_QUOTES); ExecPath=FindFileInPath(ExecPath,Token,getenv("PATH")); i=0; if (! (Flags & SPAWN_ARG0)) { argv[0]=CopyStr(argv[0],ExecPath); i=1; } for (; i < 100; i++) { ptr=GetToken(ptr,"\\S",&Token,GETTOKEN_QUOTES); if (! ptr) break; argv[i]=CopyStr(argv[i],Token); } execv(ExecPath,argv); } else result=execl("/bin/sh","/bin/sh","-c",(char *) Command,NULL); RaiseError(ERRFLAG_ERRNO, "Spawn", "Failed to execute '%s'",Command); //We'll never get to here unless something fails! DestroyString(FinalCommand); DestroyString(ExecPath); DestroyString(Token); return(result); }
int PipeCommandProcessorInit(TProcessingModule *ProcMod, const char *Args) { int result=FALSE; char *Tempstr=NULL; char *Name=NULL, *Value=NULL, *ptr; STREAM *S; ptr=GetNameValuePair(Args,"\\S","=",&Name,&Value); while (ptr) { if (strcasecmp(Name,"Command")==0) Tempstr=CopyStr(Tempstr,Value); ptr=GetNameValuePair(ptr,"\\S","=",&Name,&Value); } if (! StrLen(Tempstr) ) { DestroyString(Name); DestroyString(Value); DestroyString(Tempstr); return(FALSE); } GetToken(Tempstr,"\\S",&Name,0); Value=FindFileInPath(Value,Name,getenv("PATH")); if (! StrLen(Value) ) { DestroyString(Name); DestroyString(Value); DestroyString(Tempstr); return(FALSE); } S=STREAMSpawnCommand(Value, COMMS_BY_PIPE); ProcMod->Data=(void *) S; result=TRUE; DestroyString(Name); DestroyString(Value); DestroyString(Tempstr); return(result); }
pid_t HandlePostFileRequest(STREAM *ClientCon, char *Data) { HTTPSession *Response; STREAM *S; char *Tempstr=NULL; pid_t pid; pid=fork(); if (pid==0) { Response=ParseSessionInfo(Data); Tempstr=FindFileInPath(Tempstr,Response->Path,Response->SearchPath); Response->Path=CopyStr(Response->Path, Tempstr); if (! SwitchGroup(Response->Group)) { LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' when posting '%s'", Response->RealUser, Tempstr); } if (! SwitchUser(Response->RealUser)) { LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' when posting to document '%s'", Tempstr); LogFileFlushAll(TRUE); _exit(0); } LogToFile(Settings.LogPath,"SWITCH USER: '******' posting document '%s'", Response->RealUser, Response->Group, Tempstr); STREAMWriteLine("READY\n",ClientCon); STREAMFlush(ClientCon); HTTPServerHandlePost(ClientCon, Response); //exit 1 means that we can keep connection alive for re-use LogFileFlushAll(TRUE); if (Response->Flags & SESSION_KEEPALIVE) _exit(1); _exit(0); } else ClientCon->State |= SS_EMBARGOED; DestroyString(Tempstr); return(pid); }
pid_t HandleGetFileRequest(STREAM *ClientCon, char *Data) { HTTPSession *Response; char *Tempstr=NULL; pid_t pid; pid=fork(); if (pid==0) { Response=ParseSessionInfo(Data); Tempstr=FindFileInPath(Tempstr,Response->Path,Response->SearchPath); if (! SwitchGroup(Response->Group)) { LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' when getting document '%s'", Response->RealUser, Tempstr); } if (! SwitchUser(Response->RealUser)) { LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' when getting document '%s'", Tempstr); LogFileFlushAll(TRUE); _exit(0); } STREAMWriteLine("READY\n",ClientCon); STREAMFlush(ClientCon); HTTPServerSendDocument(ClientCon, Response, Tempstr, HEADERS_SENDFILE|HEADERS_USECACHE|HEADERS_KEEPALIVE); //exit 1 means that we can keep connection alive for re-use LogFileFlushAll(TRUE); if (Response->Flags & SESSION_KEEPALIVE) _exit(1); _exit(0); } DestroyString(Tempstr); return(pid); }
//Parse a list of packing formats and their associated commands so we can offer the user //'download as zip' in the directory webpage char *ParsePackFormats(char *RetStr, const char *Config) { char *Name=NULL, *Value=NULL, *ptr, *tptr; char *Path=NULL; RetStr=CopyStr(RetStr,""); ptr=GetNameValuePair(Config, ",",":",&Name,&Value); while (ptr) { if (StrLen(Name) && StrLen(Value)) { if (strcasecmp(Value,"internal")==0) RetStr=MCatStr(RetStr,Name,":",Value,",",NULL); else { tptr=strchr(Value,' '); if (tptr) { *tptr='\0'; tptr++; } //we don't want this to be null if strchr returns a null, otherwise it will //shorten our sting when we use MCatStr below. else tptr=""; Path=FindFileInPath(Path,Value,getenv("PATH")); if (StrLen(Path)) RetStr=MCatStr(RetStr,Name,":",Path," ", tptr, ",",NULL); } } ptr=GetNameValuePair(ptr, ",",":",&Name,&Value); } DestroyString(Name); DestroyString(Value); return(RetStr); }
pid_t HandleCGIExecRequest(STREAM *ClientCon, char *Data) { char *Tempstr=NULL, *Name=NULL, *Value=NULL; char *ScriptPath=NULL; int result, i; HTTPSession *Response; //We will never read from this stream again. Any further data will be read //by the process we spawn off ClientCon->State |= SS_EMBARGOED; Response=ParseSessionInfo(Data); CleanStr(Response->Path); CleanStr(Response->SearchPath); CleanStr(Response->StartDir); ScriptPath=FindFileInPath(ScriptPath,Response->Path,Response->SearchPath); if (access(ScriptPath,F_OK) !=0) { HTTPServerSendHTML(ClientCon, Response, "404 Not Found","Couldn't find that script."); LogToFile(Settings.LogPath,"No such script: %s in path %s = %s",Response->Path,Response->SearchPath,ScriptPath); } else if ( (access(ScriptPath,X_OK) !=0) || (! CheckScriptIntegrity(ScriptPath)) ) { HTTPServerSendHTML(ClientCon, Response, "403 Forbidden","You don't have permission for that."); LogToFile(Settings.LogPath,"Cannot execute script: %s",ScriptPath); } else { STREAMFlush(ClientCon); LogFileFlushAll(TRUE); result=fork(); if (result==0) { //do this so that when we exec the script, anything output goes to the client close(0); dup(ClientCon->in_fd); close(1); dup(ClientCon->out_fd); //must use write because stream is embargoed write(1,"READY\n",6); HTTPServerExecCGI(ClientCon, Response, ScriptPath); //exit whether script ran or not! _exit(0); } } HTTPSessionDestroy(Response); DestroyString(ScriptPath); DestroyString(Tempstr); DestroyString(Name); DestroyString(Value); //Always return STREAM_CLOSED, so that pipe gets closed regardless of exit status of //forked helper process return(STREAM_CLOSED); }
pid_t HandleWebsocketExecRequest(STREAM *ClientCon, char *Data) { char *Tempstr=NULL, *Name=NULL, *Value=NULL; char *ScriptPath=NULL; int result, i; HTTPSession *Response; //We will never read from this stream again. Any further data will be read //by the process we spawn off ClientCon->State |= SS_EMBARGOED; Response=ParseSessionInfo(Data); CleanStr(Response->Path); CleanStr(Response->SearchPath); CleanStr(Response->StartDir); ScriptPath=FindFileInPath(ScriptPath, Response->Path, Response->SearchPath); LogToFile(Settings.LogPath,"Script: Found=[%s] SearchPath=[%s] ScriptName=[%s] Arguments=[%s]", ScriptPath, Response->SearchPath, Response->Path, Response->Arguments); if (access(ScriptPath,F_OK) !=0) { LogToFile(Settings.LogPath,"No such script: %s in path %s = %s",Response->Path, Response->SearchPath, ScriptPath); } else if ( (access(ScriptPath,X_OK) !=0) || (! CheckScriptIntegrity(ScriptPath)) ) { LogToFile(Settings.LogPath,"Cannot execute script: %s", ScriptPath); } else { STREAMFlush(ClientCon); result=fork(); if (result==0) { //do this so that when we exec the script, anything output goes to the client close(0); dup(ClientCon->in_fd); close(1); dup(ClientCon->out_fd); if (! SwitchGroup(Response->Group)) { LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' to execute script: %s using handler '%s'", Response->RealUser, ScriptPath, Tempstr); } //Switch user. ALAYA WILL NOT RUN SCRIPTS AS ROOT! if (! SwitchUser(Response->RealUser)) { LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' to execute script: %s using handler '%s'", Response->RealUser, ScriptPath, Tempstr); LogFileFlushAll(TRUE); _exit(0); } if (geteuid()==0) { LogToFile(Settings.LogPath, "Failed to switch user to '%s' for running a .cgi program. Will not run programs as 'root'. Set 'DefaultUser' in config file or command line.", Response->RealUser); } else { SetupEnvironment(Response, ScriptPath); Tempstr=FindScriptHandlerForScript(Tempstr,ScriptPath); if (Tempstr) LogToFile(Settings.LogPath,"Execute script: %s using handler '%s'",ScriptPath,Tempstr); else LogToFile(Settings.LogPath,"Execute script: %s QUERY_STRING= '%s'",ScriptPath,getenv("QUERY_STRING")); //Only do this late! Otherwise logging won't work. for (i=3; i < 1000; i++) close(i); if (StrLen(Tempstr)) execl(Tempstr, Tempstr, ScriptPath,NULL); else execl(ScriptPath,ScriptPath,NULL); //Logging won't work after we've closed all the file descriptors! LogToFile(Settings.LogPath,"Cannot execute script: %s",ScriptPath); } LogFileFlushAll(TRUE); _exit(0); } else { } } HTTPSessionDestroy(Response); DestroyString(ScriptPath); DestroyString(Tempstr); DestroyString(Name); DestroyString(Value); //Always return STREAM_CLOSED, so that pipe gets closed regardless of exit status of //forked helper process return(STREAM_CLOSED); }
DWORD GetWbclientDir( PCSTR pSmbdPath, PSTR* ppDir ) { PCSTR ppBackupPaths[] = { "/usr/lib", "/usr/lib64", NULL, }; DWORD index = 0; DWORD error = 0; BOOLEAN exists = 0; PSTR pFoundPath = NULL; PSTR pCommandLine = NULL; PCSTR ppArgs[] = { "/bin/sh", "-c", NULL, NULL }; PSTR pSambaLibdir = NULL; *ppDir = NULL; // First see if libwbclient.so.0 is in Samba's libdir. There may be two // copies of libwbclient.so.0 because of different architectures. This will // identify which one is the primary one. error = LwAllocateStringPrintf( &pCommandLine, "%s -b | grep LIBDIR:", pSmbdPath ); BAIL_ON_LSA_ERROR(error); ppArgs[2] = pCommandLine; error = CaptureOutputWithStderr( "/bin/sh", ppArgs, &pSambaLibdir, NULL); BAIL_ON_LSA_ERROR(error); LwStripWhitespace( pSambaLibdir, TRUE, TRUE); if (strstr(pSambaLibdir, ": ")) { char *pValueStart = strstr(pSambaLibdir, ": ") + 2; memmove( pSambaLibdir, pValueStart, strlen(pSambaLibdir) - (pValueStart - pSambaLibdir) + 1); } error = FindFileInPath( WBCLIENT_FILENAME, pSambaLibdir, &pFoundPath); if (error == ERROR_FILE_NOT_FOUND) { // Fall back to trying the two standard system paths error = FindFileInPath( WBCLIENT_FILENAME, "/usr/lib:/usr/lib64", &pFoundPath); if (error == ERROR_FILE_NOT_FOUND) { error = 0; } } BAIL_ON_LSA_ERROR(error); if (pFoundPath) { pFoundPath[strlen(pFoundPath) - (sizeof(WBCLIENT_FILENAME) -1) - 1] = 0; *ppDir = pFoundPath; pFoundPath = NULL; goto cleanup; } // Could not find an existing libwbclient.so.0. This could be a Samba 3.0.x // build. Just stick the file in a system path. for (index = 0; ppBackupPaths[index]; index++) { error = LwCheckFileTypeExists( ppBackupPaths[index], LWFILE_DIRECTORY, &exists); BAIL_ON_LSA_ERROR(error); if (exists) { error = LwAllocateString(ppBackupPaths[index], ppDir); BAIL_ON_LSA_ERROR(error); goto cleanup; } } // Could not find the system library paths. error = ERROR_FILE_NOT_FOUND; BAIL_ON_LSA_ERROR(error); cleanup: LW_SAFE_FREE_STRING(pFoundPath); LW_SAFE_FREE_STRING(pCommandLine); LW_SAFE_FREE_STRING(pSambaLibdir); return error; }
int main( int argc, char *argv[] ) { enum { UNSET, SHOW_HELP, CHECK_VERSION, INSTALL, UNINSTALL } mode = UNSET; PCSTR pSmbdPath = NULL; PSTR pFoundSmbdPath = NULL; DWORD error = 0; DWORD argIndex = 0; LW_RTL_LOG_LEVEL logLevel = LW_RTL_LOG_LEVEL_ERROR; PCSTR pErrorSymbol = NULL; PSTR pVersion = NULL; BOOLEAN smbdExists = FALSE; BOOLEAN force = FALSE; for (argIndex = 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "--check-version")) { if (mode == UNSET) { mode = CHECK_VERSION; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--install")) { if (mode == UNSET) { mode = INSTALL; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--uninstall")) { if (mode == UNSET) { mode = UNINSTALL; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--force")) { if (mode == INSTALL || mode== UNINSTALL) { force = TRUE; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--loglevel")) { argIndex++; if (argIndex >= argc) { error = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(error); } if (!strcmp(argv[argIndex], "error")) { logLevel = LW_RTL_LOG_LEVEL_ERROR; } else if (!strcmp(argv[argIndex], "warning")) { logLevel = LW_RTL_LOG_LEVEL_WARNING; } else if (!strcmp(argv[argIndex], "info")) { logLevel = LW_RTL_LOG_LEVEL_INFO; } else if (!strcmp(argv[argIndex], "verbose")) { logLevel = LW_RTL_LOG_LEVEL_VERBOSE; } else if (!strcmp(argv[argIndex], "debug")) { logLevel = LW_RTL_LOG_LEVEL_DEBUG; } else { error = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(error); } } else if (argIndex == argc - 1) { pSmbdPath = argv[argIndex]; } else { mode = SHOW_HELP; } } if (mode == UNSET || mode == SHOW_HELP) { ShowUsage(argv[0]); goto cleanup; } LwRtlLogSetCallback(LogCallback, NULL); LwRtlLogSetLevel(logLevel); if (pSmbdPath == NULL) { PCSTR pSearchPath = "/usr/sbin:/usr/local/sbin:/usr/local/samba/sbin:/opt/csw/samba/sbin:/opt/sfw/samba/sbin:/opt/csw/bin:/usr/local/bin"; error = FindFileInPath( "smbd", pSearchPath, &pFoundSmbdPath); if (error == ERROR_FILE_NOT_FOUND) { LW_RTL_LOG_ERROR("The smbd file could not be automatically found on your system. The search path was '%s'. Pass the correct location as the last argument to this program.", pSearchPath); } BAIL_ON_LSA_ERROR(error); pSmbdPath = pFoundSmbdPath; } error = LwCheckFileTypeExists( pSmbdPath, LWFILE_REGULAR, &smbdExists); BAIL_ON_LSA_ERROR(error); if (!smbdExists) { error = LwCheckFileTypeExists( pSmbdPath, LWFILE_SYMLINK, &smbdExists); BAIL_ON_LSA_ERROR(error); } if (!smbdExists) { LW_RTL_LOG_ERROR("Smbd file not found at path '%s'", pSmbdPath); } error = CheckSambaVersion(pSmbdPath, &pVersion); if (force == FALSE) { BAIL_ON_LSA_ERROR(error); } if (mode == CHECK_VERSION) { fprintf(stderr, "Samba version supported\n"); } else if (mode == INSTALL) { if (geteuid() != 0) { fprintf(stderr, "Please use the root account to install the Samba interop libraries\n"); goto cleanup; } error = InstallWbclient(pSmbdPath); BAIL_ON_LSA_ERROR(error); if (pVersion && strncmp(pVersion, "3.0.", sizeof("3.0.") - 1) == 0) { // Only Samba 3.0.x needs this error = InstallLwiCompat(pSmbdPath); BAIL_ON_LSA_ERROR(error); } error = SynchronizePassword( pSmbdPath); BAIL_ON_LSA_ERROR(error); fprintf(stderr, "Install successful\n"); } else if (mode == UNINSTALL) { if (geteuid() != 0) { fprintf(stderr, "Please use the root account to uninstall the Samba interop libraries\n"); goto cleanup; } error = UninstallWbclient(pSmbdPath); BAIL_ON_LSA_ERROR(error); error = UninstallLwiCompat(pSmbdPath); BAIL_ON_LSA_ERROR(error); error = DeletePassword( pSmbdPath); BAIL_ON_LSA_ERROR(error); fprintf(stderr, "Uninstall successful\n"); } else { fprintf(stderr, "Uninstall mode not implemented\n"); error = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(error); } cleanup: LW_SAFE_FREE_STRING(pFoundSmbdPath); LW_SAFE_FREE_STRING(pVersion); if (error) { pErrorSymbol = LwWin32ErrorToName(error); if (pErrorSymbol != NULL) { fprintf(stderr, "Error: %s\n", pErrorSymbol); } else { fprintf(stderr, "Unknown error\n"); } } return error; }
int main( int argc, char *argv[] ) { enum { UNSET, SHOW_HELP, CHECK_VERSION, INSTALL, UNINSTALL } mode = UNSET; PCSTR pSmbdPath = NULL; PSTR pFoundSmbdPath = NULL; DWORD error = 0; DWORD argIndex = 0; LW_RTL_LOG_LEVEL logLevel = LW_RTL_LOG_LEVEL_ERROR; PCSTR pErrorSymbol = NULL; for (argIndex = 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "--check-version")) { if (mode == UNSET) { mode = CHECK_VERSION; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--install")) { if (mode == UNSET) { mode = INSTALL; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--uninstall")) { if (mode == UNSET) { mode = UNINSTALL; } else { mode = SHOW_HELP; } } else if (!strcmp(argv[argIndex], "--loglevel")) { argIndex++; if (argIndex >= argc) { error = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(error); } if (!strcmp(argv[argIndex], "error")) { logLevel = LW_RTL_LOG_LEVEL_ERROR; } else if (!strcmp(argv[argIndex], "warning")) { logLevel = LW_RTL_LOG_LEVEL_WARNING; } else if (!strcmp(argv[argIndex], "info")) { logLevel = LW_RTL_LOG_LEVEL_INFO; } else if (!strcmp(argv[argIndex], "verbose")) { logLevel = LW_RTL_LOG_LEVEL_VERBOSE; } else if (!strcmp(argv[argIndex], "debug")) { logLevel = LW_RTL_LOG_LEVEL_DEBUG; } else { error = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(error); } } else if (argIndex == argc - 1) { pSmbdPath = argv[2]; } else { mode = SHOW_HELP; } } if (mode == UNSET || mode == SHOW_HELP) { ShowUsage(argv[0]); goto cleanup; } LwRtlLogSetCallback(LogCallback, NULL); LwRtlLogSetLevel(logLevel); if (pSmbdPath == NULL) { error = FindFileInPath( "smbd", "/usr/sbin", &pFoundSmbdPath); BAIL_ON_LSA_ERROR(error); pSmbdPath = pFoundSmbdPath; } error = CheckSambaVersion(pSmbdPath); BAIL_ON_LSA_ERROR(error); if (mode == CHECK_VERSION) { fprintf(stderr, "Samba version supported\n"); } else if (mode == INSTALL) { error = InstallWbclient(pSmbdPath); BAIL_ON_LSA_ERROR(error); error = InstallLwiCompat(pSmbdPath); BAIL_ON_LSA_ERROR(error); error = SynchronizePassword( pSmbdPath); BAIL_ON_LSA_ERROR(error); fprintf(stderr, "Install successful\n"); } else if (mode == UNINSTALL) { error = UninstallWbclient(pSmbdPath); BAIL_ON_LSA_ERROR(error); error = UninstallLwiCompat(pSmbdPath); BAIL_ON_LSA_ERROR(error); error = DeletePassword( pSmbdPath); BAIL_ON_LSA_ERROR(error); fprintf(stderr, "Uninstall successful\n"); } else { fprintf(stderr, "Uninstall mode not implemented\n"); error = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(error); } cleanup: LW_SAFE_FREE_STRING(pFoundSmbdPath); if (error) { pErrorSymbol = LwWin32ErrorToName(error); if (pErrorSymbol != NULL) { fprintf(stderr, "Error: %s\n", pErrorSymbol); } else { fprintf(stderr, "Unknown error\n"); } } return error; }