From 1cdfa35ada9af5195c4e47e07b68b44d0c2c090d Mon Sep 17 00:00:00 2001 From: Darren VanBuren Date: Tue, 7 Mar 2017 21:10:54 -0800 Subject: [PATCH] Add qtpasswd source --- qtpasswd.tproj/QTSSPasswd.cpp | 1278 +++++++++++++++++++++++++++++++++ 1 file changed, 1278 insertions(+) create mode 100644 qtpasswd.tproj/QTSSPasswd.cpp diff --git a/qtpasswd.tproj/QTSSPasswd.cpp b/qtpasswd.tproj/QTSSPasswd.cpp new file mode 100644 index 0000000..963394f --- /dev/null +++ b/qtpasswd.tproj/QTSSPasswd.cpp @@ -0,0 +1,1278 @@ +/* + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + */ + /* + Modified htpasswd.c + format of the file: + username:crypt(password):MD5(username:realm:password) +*/ + +/****************************************************************************** + ****************************************************************************** + * NOTE! This program is not safe as a setuid executable! Do not make it + * setuid! + ****************************************************************************** + *****************************************************************************/ + +#ifndef __Win32__ +#include +#include +#include +#include +#ifndef __MacOSX__ +#include "getopt.h" +#endif +#include +#else +#include "getopt.h" +#include "OSHeaders.h" +#include +#include +#endif + +#ifndef __Win32__ + #include + #include +#endif + +#ifdef __solaris__ + #include +#endif + +#include +#include +#include +#include "SafeStdLib.h" +#include +#include "StrPtrLen.h" +#include "md5digest.h" +#include "md5.h" +#include "../defaultPaths.h" +#include "../revision.h" + +#ifdef __linux__ + #include + #include +#endif + +#ifndef CHARSET_EBCDIC +#define LF 10 +#define CR 13 +#else /*CHARSET_EBCDIC*/ +#define LF '\n' +#define CR '\r' +#endif /*CHARSET_EBCDIC*/ + +#define MAX_STRING_LEN 255 +#define MAX_LINE_LEN 5120 +#define MAX_PASSWORD_LEN 80 + +const char* kDefaultQTPasswdFilePath = DEFAULTPATHS_ETC_DIR "qtusers"; +const char* kDefaultQTGroupsFilePath = DEFAULTPATHS_ETC_DIR "qtgroups"; +const char* kDefaultRealmString = "Streaming Server"; +const char* kDefaultFileOwner = "qtss"; + +char *tempUsersFile = NULL; +char *tempGroupsFile = NULL; + +char *fileOwner = (char*) kDefaultFileOwner; + +FILE *usersFilePtr = NULL, *tempUsersFilePtr = NULL, *passFilePtr = NULL, *groupsFilePtr = NULL; + +void SetPrivileges(char *filePath); + +static void closeFile(FILE **filePtr) +{ + if (filePtr && *filePtr) + { fclose(*filePtr); + *filePtr = NULL; + } + +} + +static void CleanUp(void) +{ + closeFile(&usersFilePtr); + closeFile(&tempUsersFilePtr); + closeFile(&passFilePtr); + closeFile(&groupsFilePtr); + + + if(tempUsersFile) + { + unlink(tempUsersFile); + delete [] tempUsersFile; + } + + if(tempGroupsFile) + { + unlink(tempGroupsFile); + delete [] tempGroupsFile; + } +} + +/* + * CleanupAndExit: Deletes the temp file + * and exits with code 1 + * + */ +static void CleanupAndExit(void) +{ + #ifndef __Win32__ + if (EACCES == errno || EPERM == errno) + { + qtss_fprintf(stderr, "You must use the sudo command to edit the users and groups files.\n"); + qtss_fprintf(stderr, "Example: sudo qtpasswd user\n"); + } +#endif + + CleanUp(); + exit(1); +} + +/* + * CopyString: Returns a malloc'd copy + * of inString + */ +static char *CopyString(char *inString) +{ + char *outString = (char *) malloc(strlen(inString) + 1); + strcpy(outString, inString); + return outString; +} + +/* + * EatWhitespace: Goes past any leading whitespace + * + */ +static void EatWhitespace(char *line) +{ + int x = 0, y = 0; + + while (line[x] == ' ') + x++; + + while ((line[y++] = line[x++])); +} + +/* + * GetWord: Reads all characters from the + * line until the stop character + * and returns it in word + */ +static void GetWord(char *word, char *line, char stop) +{ + int x = 0, y; + + for (x = 0; ((line[x]) && (line[x] != stop)); x++) + word[x] = line[x]; + + word[x] = '\0'; + if (line[x]) + ++x; + y = 0; + + while ((line[y++] = line[x++])); +} + +/* + * GetLine: Reads from file f, n characters + * or until newline and returns + * puts the line in s. + */ +static int GetLine(char *s, int n, FILE *f) +{ + register int i = 0; + + while (1) { + s[i] = (char) fgetc(f); + + if (s[i] == CR) + s[i] = fgetc(f); + + if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +/* + * PutLine: Writes string l to f and + * puts a '\n' at the end + */ +static void PutLine(FILE *f, char *l) +{ + int x; + + for (x = 0; l[x]; x++) + fputc(l[x], f); + fputc('\n', f); +} + +/* + * PutWord: Writes string l to f + * and puts the endChar + * at the end + */ +static void PutWord(FILE *f, char *l, char endChar) +{ + int x; + + for (x = 0; l[x]; x++) + fputc(l[x], f); + fputc(endChar, f); +} + +#if __Win32__ +/* + * Windows lacks getpass(). So we'll re-implement it here. + */ + +static char *getpass(const char *prompt) +{ + static char password[MAX_PASSWORD_LEN + 1]; + int n = 0; + + fputs(prompt, stderr); + + while ((password[n] = _getch()) != '\r') { + + if(n == MAX_PASSWORD_LEN) { + fputs("password can't be longer than MAX_PASSWORD_LEN chars.\n", stderr); + fputs(prompt, stderr); + for(n = 0; n < (MAX_PASSWORD_LEN + 1); n++) + password[n] = '\0'; + n = 0; + continue; + } + + if (password[n] >= ' ' && password[n] <= '~') { + n++; + qtss_printf("*"); + } + else { + qtss_printf("\n"); + fputs(prompt, stderr); + n = 0; + } + } + + password[n] = '\0'; + qtss_printf("\n"); + + return (char *) &password; +} + +#endif + +/* + * Digest: Returns the MD5 hash of user:realm:password + */ +static char* Digest(char *user, char *passwd, char *realm) +{ + StrPtrLen userSPL(user), passwdSPL(passwd), realmSPL(realm), hashHex16Bit, hashSPL; + CalcMD5HA1(&userSPL, &realmSPL, &passwdSPL, &hashHex16Bit); // memory allocated for hashHex16Bit.Ptr + HashToString((unsigned char *)hashHex16Bit.Ptr, &hashSPL); // memory allocated for hashSPL.Ptr + char* digestStr = hashSPL.GetAsCString(); + delete [] hashSPL.Ptr; // freeing memory allocated in above calls + delete [] hashHex16Bit.Ptr; + return digestStr; +} + +/* + * AddPasswordWithoutPrompt: Adds the entry in the file + * user:crpytofpassword:md5hash(user:realm:password) + */ +static void AddPasswordWithoutPrompt(char *user, char* password, char* realm, FILE *f) +{ + char salt[9]; + + (void) srand((int) time((time_t *) NULL)); + to64(&salt[0], rand(), 8); + salt[8] = '\0'; + + char cpw[120], *dpw; + int crpwLen = 0; + +#ifdef __Win32__ + MD5Encode((char *)password, (char *)salt, cpw, sizeof(cpw)); +#else + char *crpw = (char *)crypt(password, salt); // cpw is crypt of password + crpwLen = ::strlen(crpw); + strncpy(cpw, crpw, crpwLen); + cpw[crpwLen] = '\0'; +#endif + + dpw = (char *)Digest(user, password, realm); // dpw is digest of password + + qtss_fprintf(f, "%s:%s:%s\n", user, cpw, dpw); +} + +/* + * AddPassword: Prompts the user for a password + * and adds the entry in the file + * user:crpytofpassword:md5hash(user:realm:password) + */ +static void AddPassword(char *user, char* realm, FILE *f) +{ + char *pw, *crpw, cpw[120], salt[9], *dpw; + int len = 0, i = 0, crpwLen = 0; + char *checkw; + + pw = CopyString((char *) getpass("New password:")); + /* check for a blank password */ + len = strlen(pw); + checkw = new char[len+1]; + for(i = 0; i < len; i++) + checkw[i] = ' '; + checkw[len] = '\0'; + + if(strcmp(pw, checkw) == 0) + { + qtss_fprintf(stderr, "Password cannot be blank, sorry.\n"); + delete(checkw); + CleanupAndExit(); + } + delete(checkw); + + if (strcmp(pw, (char *) getpass("Re-type new password:"))) + { + qtss_fprintf(stderr, "They don't match, sorry.\n"); + CleanupAndExit(); + } + + (void) srand((int) time((time_t *) NULL)); + to64(&salt[0], rand(), 8); + salt[8] = '\0'; + +#ifdef __Win32__ + MD5Encode((char *)pw, (char *)salt, cpw, sizeof(cpw)); +#else + crpw = (char *)crypt(pw, salt); // cpw is crypt of password + crpwLen = ::strlen(crpw); + strncpy(cpw, crpw, crpwLen); + cpw[crpwLen] = '\0'; +#endif + + dpw = (char *)Digest(user, pw, realm); // dpw is digest of password + + qtss_fprintf(f, "%s:%s:%s\n", user, cpw, dpw); + free(pw); // Do after cpw and dpw are used. +} + +/* + * Usage: Prints the usage and calls exit + */ +static void usage(void) +{ + qtss_fprintf(stderr, " qtpasswd %s built on: %s\n", kVersionString, __DATE__ ", "__TIME__); + qtss_fprintf(stderr, " Usage: qtpasswd [-F] [-f filename] [-c] [-g groupsfilename] [-r realm] [-p password] [-P passwordfile] [-A group] [-D group] [-d] [username]\n"); + qtss_fprintf(stderr, " -F Don't ask for confirmation when deleting users or overwriting existing files.\n"); + qtss_fprintf(stderr, " -f Password file to manipulate (Default is \"%s\").\n", kDefaultQTPasswdFilePath); + qtss_fprintf(stderr, " -c Create new file.\n"); + qtss_fprintf(stderr, " -g Groups file to manipulate (Default is \"%s\"). If not found, will create one when necessary.\n", kDefaultQTGroupsFilePath); + qtss_fprintf(stderr, " -r The realm name to use when creating a new file via \"-c\" (Default is \"%s\").\n", kDefaultRealmString); + qtss_fprintf(stderr, " -p Allows entry of password at command line rather than prompting for it.\n"); + qtss_fprintf(stderr, " -P File to read the password from rather than prompting for it.\n"); + qtss_fprintf(stderr, " -d Delete the user. (Deletes the user from all groups)\n"); + qtss_fprintf(stderr, " -A Add user to group. Will create group automatically if group is not already present.\n"); + qtss_fprintf(stderr, " -D Delete the user from the group.\n"); + qtss_fprintf(stderr, " -C Create new group. Do not specify username with this option.\n"); + qtss_fprintf(stderr, " -R Delete the group. Do not specify username with this option.\n"); + qtss_fprintf(stderr, " -O Set the owner of the file (Default is \"%s\").\n", kDefaultFileOwner); + qtss_fprintf(stderr, " -h Displays usage.\n"); + qtss_fprintf(stderr, " -v Displays usage.\n"); + qtss_fprintf(stderr, " -? Displays usage.\n"); + qtss_fprintf(stderr, " Note:\n"); + qtss_fprintf(stderr, " The username must always be specified except when -C and -R options are used to create/delete group.\n"); + qtss_fprintf(stderr, " Usernames cannot be more than %d characters long and must not include a colon [:].\n", MAX_STRING_LEN); + qtss_fprintf(stderr, " Passwords cannot be more than %d characters long.\n", MAX_PASSWORD_LEN); + qtss_fprintf(stderr, " Groups cannot be more than %d characters long and must not include a colon [:].\n", MAX_STRING_LEN); + qtss_fprintf(stderr, " If the username/password contains whitespace or characters that may be\n"); + qtss_fprintf(stderr, " interpreted by the shell please enclose it in single quotes,\n"); + qtss_fprintf(stderr, " to prevent it from being interpolated.\n"); + qtss_fprintf(stderr, "\n"); + exit(1); +} + +/* unused routine +static char* SetTempPath(char* bufferToSet, int bufferLen, char* base, int baseLen, char id) +{ + if (bufferLen > 0 && bufferToSet != NULL) + memset(bufferToSet, 0, bufferLen); + + if (baseLen + 2 > bufferLen) + return bufferToSet; + + strcpy(bufferToSet, base); + bufferToSet[baseLen] = id; + bufferToSet[baseLen + 1] = '\0'; + + return bufferToSet; +} +*/ + + +static void AddOrDeleteUserFromGroup(int add, char *userName, char *groupName, char *inGroupsFilePath, char *inTempGroupsFilePath) +{ + char line[MAX_LINE_LEN]; + char lineFromFile[MAX_LINE_LEN]; + char groupNameFromFile[MAX_STRING_LEN + 1]; + bool foundGroup = false; + char userInGroup[MAX_STRING_LEN + 1]; + bool addedUserToGroup = false; + FILE *groupsFilePtr, *tempGroupsFilePtr; + +#if __Win32__ + char groupBackupPath[1024] = ""; + char groupTempPath[1024] = ""; + + int pathBaseLen = strlen(inTempGroupsFilePath); + + (void) SetTempPath(groupBackupPath, sizeof(groupBackupPath), inTempGroupsFilePath, pathBaseLen, 'b'); + inTempGroupsFilePath = SetTempPath(groupTempPath, sizeof(groupTempPath), inTempGroupsFilePath, pathBaseLen, 'x'); +#endif + + if (!(groupsFilePtr = fopen(inGroupsFilePath, "r"))) + { + if (add) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open groups file %s for reading. (err=%d:%s)\n", inGroupsFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + else + return; + } + + if (!(tempGroupsFilePtr = fopen(inTempGroupsFilePath, "w"))) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open temp groups file. (err=%d %s)\n", errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + + while (!(GetLine(line, MAX_LINE_LEN, groupsFilePtr))) + { + //write comments and blank lines out to temp file + if (foundGroup || (line[0] == '#') || (line[0] == 0)) + { + PutLine(tempGroupsFilePtr, line); + continue; + } + + strcpy(lineFromFile, line); + EatWhitespace(lineFromFile); + GetWord(groupNameFromFile, lineFromFile, ':'); + + //if it's not the group we're looking for, write the line out to the temp file + if ((groupName != NULL) && strcmp(groupName, groupNameFromFile) != 0) + { + PutLine(tempGroupsFilePtr, line); + continue; + } + else + { + PutWord(tempGroupsFilePtr, groupNameFromFile, ':'); + + while (true) + { + userInGroup[0] = '\0'; + EatWhitespace(lineFromFile); + GetWord(userInGroup, lineFromFile, ' '); + + if (userInGroup[0] == '\0') + break; + + if (strcmp(userName, userInGroup) != 0) + PutWord(tempGroupsFilePtr, userInGroup, ' '); + else if(add) + { + PutWord(tempGroupsFilePtr, userInGroup, ' '); + addedUserToGroup = true; + } + } + + if (add && !addedUserToGroup) + { + PutWord(tempGroupsFilePtr, userName, ' '); + addedUserToGroup = true; + } + + fputc('\n', tempGroupsFilePtr); + + if (groupName != NULL) + foundGroup = true; + } + } + + if (add && !addedUserToGroup) + { + PutWord(tempGroupsFilePtr, groupName, ':'); + PutLine(tempGroupsFilePtr, userName); + } + closeFile(&groupsFilePtr); + closeFile(&tempGroupsFilePtr); + + // Rename the temp groups file to the groups file + +#if __Win32__ + unlink(groupBackupPath); + rename(inGroupsFilePath, groupBackupPath); + unlink(inGroupsFilePath); + if (rename(inTempGroupsFilePath, inGroupsFilePath) != 0) + { + rename(groupBackupPath, inGroupsFilePath); + unlink(groupBackupPath); + perror("rename failed with error"); + CleanupAndExit(); + } + unlink(groupBackupPath); + unlink(inTempGroupsFilePath); + +#else + if (rename(inTempGroupsFilePath, inGroupsFilePath) != 0) + { + perror("rename failed with error"); + CleanupAndExit(); + } +#endif + + + SetPrivileges(inGroupsFilePath); + +} + +static void AddOrDeleteGroup(int add, char *groupName, char *inGroupsFilePath, char *inTempGroupsFilePath) +{ + char line[MAX_LINE_LEN]; + char lineFromFile[MAX_LINE_LEN]; + char groupNameFromFile[MAX_STRING_LEN + 1]; + bool foundGroup = false; + FILE *groupsFilePtr, *tempGroupsFilePtr; + bool addedGroup = false; + +#if __Win32__ + char groupBackupPath[1024] = ""; + char groupTempPath[1024] = ""; + + int pathBaseLen = strlen(inTempGroupsFilePath); + + (void) SetTempPath(groupBackupPath, sizeof(groupBackupPath), inTempGroupsFilePath, pathBaseLen, 'b'); + inTempGroupsFilePath = SetTempPath(groupTempPath, sizeof(groupTempPath), inTempGroupsFilePath, pathBaseLen, 'x'); +#endif + + if (!(groupsFilePtr = fopen(inGroupsFilePath, "r"))) + { + if (add) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open groups file %s for reading. (err=%d:%s)\n", inGroupsFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + else + return; + } + + if (!(tempGroupsFilePtr = fopen(inTempGroupsFilePath, "w"))) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open temp groups file. (err=%d %s)\n", errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + + while (!(GetLine(line, MAX_LINE_LEN, groupsFilePtr))) + { + //write comments and blank lines out to temp file + if (foundGroup || (line[0] == '#') || (line[0] == 0)) + { + PutLine(tempGroupsFilePtr, line); + continue; + } + + strcpy(lineFromFile, line); + EatWhitespace(lineFromFile); + GetWord(groupNameFromFile, lineFromFile, ':'); + + //if it's not the group we're looking for, write the line out to the temp file + if ((groupName != NULL) && strcmp(groupName, groupNameFromFile) != 0) + { + PutLine(tempGroupsFilePtr, line); + continue; + } + else if (add) // if we are trying to add the group and it already exists, leave it in + { + PutLine(tempGroupsFilePtr, line); + addedGroup = true; + } + + foundGroup = true; + } + + if (add && !addedGroup) + { + PutWord(tempGroupsFilePtr, groupName, ':'); + fputc('\n', tempGroupsFilePtr); + } + + closeFile(&groupsFilePtr); + closeFile(&tempGroupsFilePtr); + + + // Rename the temp groups file to the groups file +#if __Win32__ + _unlink(groupBackupPath); //make sure it is clean + rename(inGroupsFilePath, groupBackupPath); //move the groups file to the new name + _unlink(inGroupsFilePath);// make sure it is clean + + if (rename(inTempGroupsFilePath, inGroupsFilePath) != 0) // move the temp to the real name + { + perror("rename failed with error"); + rename(groupBackupPath, inGroupsFilePath); + unlink(groupBackupPath); + unlink(inTempGroupsFilePath); + CleanupAndExit(); + } + unlink(groupBackupPath); //clean up + unlink(inTempGroupsFilePath); +#else + if (rename(inTempGroupsFilePath, inGroupsFilePath) != 0) + { + perror("rename failed with error"); + CleanupAndExit(); + } +#endif + + } + +/* Allocates memory; remember to delete it afterwards */ +char* GetTempFileAtPath(char* templatePath, int templatePathLength) +{ + char* tempFile = new char[templatePathLength]; + memcpy(tempFile, templatePath, templatePathLength); + char* theResultTempFile = NULL; + +#ifdef __Win32__ + theResultTempFile = mktemp(tempFile); +#else + int theErr = mkstemp(tempFile); + if (theErr != -1) + theResultTempFile = tempFile; +#endif + + if (theResultTempFile == NULL) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not create a temp file at %s. (err=%d:%s)\n", tempFile, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + + return theResultTempFile; +} + +int main(int argc, char *argv[]) +{ + //char line[MAX_STRING_LEN + 1]; + char line[MAX_LINE_LEN]; + char lineFromFile[MAX_LINE_LEN]; + char usernameFromFile[MAX_STRING_LEN + 1]; + char realmFromFile[MAX_STRING_LEN + 1]; + int found; + int result; + static char choice[81]; + + int doCreateNewFile = 0; + int doDeleteUser = 0; + int addUserToGroup = 0; + int deleteUserFromGroup = 0; + int createGroup = 0; + int deleteGroup = 0; + int confirmPotentialDamage = 1; + char* qtusersFilePath = NULL; + char* qtgroupsFilePath = NULL; + char* userName = NULL; + char* groupName = NULL; + char* realmString = NULL; + char* password = NULL; + char* passwordFilePath = NULL; + int ch; + extern char* optarg; + extern int optind; + + /* Read command line arguments */ + while ((ch = getopt(argc, argv, "O:f:cg:r:p:P:A:D:C:R:dFhv?")) != EOF) + { + switch(ch) + { + + case 'f': + qtusersFilePath = CopyString(optarg); + break; + + case 'c': + doCreateNewFile = 1; + break; + + case 'g': + qtgroupsFilePath = CopyString(optarg); + break; + + case 'r': + realmString = CopyString(optarg); + if (::strlen(realmString) > MAX_STRING_LEN) + { + qtss_fprintf(stderr, "Realm cannot have more than %d characters.\n", MAX_STRING_LEN); + qtss_printf("Exiting! \n"); + exit(1); + } + break; + + case 'p': + password = CopyString(optarg); + ::memset(optarg, 0, ::strlen(optarg)); + + if (::strlen(password) > MAX_PASSWORD_LEN) + { + qtss_fprintf(stderr, "Password cannot have more than %d characters.\n", MAX_PASSWORD_LEN); + qtss_printf("Exiting! \n"); + exit(1); + } + break; + + case 'O': + fileOwner = CopyString(optarg); + break; + + case 'P': + passwordFilePath = CopyString(optarg); + break; + + case 'A': + groupName = CopyString(optarg); + addUserToGroup = 1; + break; + + case 'D': + groupName = CopyString(optarg); + deleteUserFromGroup = 1; + break; + + case 'C': + groupName = CopyString(optarg); + createGroup = 1; + break; + + case 'R': + groupName = CopyString(optarg); + deleteGroup = 1; + break; + + case 'd': + doDeleteUser = 1; + break; + + case 'F': + confirmPotentialDamage = 0; + break; + + case 'h': + case 'v': + case '?': + default: + usage(); + break; + } + } + + /* If password is to be read from a file, check validity of the password */ + if ((password == NULL) && (passwordFilePath != NULL)) + { + if ((passFilePtr = fopen(passwordFilePath, "r")) != NULL ) + { + char passline[MAX_STRING_LEN]; + char passFromFile[MAX_STRING_LEN]; + int passLen = 0; + + ::memset(passline, 0, MAX_STRING_LEN); + ::memset(passFromFile, 0, MAX_STRING_LEN); + + GetLine(passline, MAX_STRING_LEN, passFilePtr); + + if (passline[0] == '\'') // if it is single quoted, read until the end single quote + GetWord(passFromFile, (passline + 1), '\''); + else if (passline[0] == '"') // if it is double quoted, read until the end double quote + GetWord(passFromFile, (passline + 1), '"'); + else // if it is not quoted, read until the first whitespace + GetWord(passFromFile, passline, ' '); + + passLen = ::strlen(passFromFile); + + if (passLen == 0) + { + qtss_fprintf(stderr, "Password in file %s is blank.\n", passwordFilePath); + qtss_printf("Exiting! \n"); + exit(1); + } + else if (passLen > MAX_PASSWORD_LEN) + { + qtss_fprintf(stderr, "Password in file %s has more than %d characters. Cannot accept password.\n", passwordFilePath, MAX_PASSWORD_LEN); + qtss_printf("Exiting! \n"); + exit(1); + } + else + password = CopyString(passFromFile); + + closeFile(&passFilePtr); + } + } + + /* deleting a user and (creating a file or setting a password) don't make sense together */ + if ( doDeleteUser && (doCreateNewFile || password != NULL) ) + { + qtss_fprintf(stderr, "Cannot use the -c option (to create the file) with the -d option (to delete the user).\n"); + qtss_printf("Exiting! \n"); + usage(); + } + + /* realm name only makes sense when creating a new password file */ + if ( !doCreateNewFile && (realmString != NULL) ) + { + qtss_fprintf(stderr, "Can use the -r option only with the -c option (when creating the file).\n"); + qtss_printf("Exiting! \n"); + usage(); + } + + /* group name checks */ + if (groupName != NULL) + { + /* check length < MAX_STRING_LEN */ + if (::strlen(groupName) > MAX_STRING_LEN) + { + qtss_fprintf(stderr, "Group name cannot have more than %d characters.\n", MAX_STRING_LEN); + qtss_printf("Exiting! \n"); + exit(1); + } + + /* check for : */ + if (strchr(groupName, ':') != NULL) + { + qtss_printf("Group name cannot contain a ':' character.\n"); + qtss_printf("Exiting! \n"); + exit(1); + } + + /* can't add user to group and delete user from a group at the same time */ + if (addUserToGroup && deleteUserFromGroup) + { + qtss_printf("Cannot add to or delete from a group at the same time (use either -A or -D option, not both!).\n"); + qtss_printf("Exiting! \n"); + exit(1); + } + + /* can't create and delete group at the same time */ + if (createGroup && deleteGroup) + { + qtss_printf("Cannot create new group and delete group at the same time (use either -C or -R option, not both!).\n"); + qtss_printf("Exiting! \n"); + exit(1); + } + } + + /* Read in the username */ + if (argv[optind] != NULL) + { + /* If group needs to be created or deleted, username will be ignored. */ + if (createGroup || deleteGroup) + { + qtss_fprintf(stderr, "Warning: username cannot be specified with -C or -R option and will be ignored!\n"); + } + else + { + userName = CopyString(argv[optind]); + + /* check length < MAX_STRING_LEN */ + if (::strlen(userName) > MAX_STRING_LEN) + { + qtss_fprintf(stderr, "Username cannot have more than %d characters.\n", MAX_STRING_LEN); + qtss_printf("Exiting! \n"); + exit(1); + } + } + } + else + { + /* Exit if username is not given, unless a group has to be created or deleted. */ + if (!createGroup && !deleteGroup) + { + qtss_fprintf(stderr, "Username not given!\n"); + qtss_printf("Exiting! \n"); + usage(); + } + } + + if (confirmPotentialDamage && doDeleteUser) + { + qtss_printf("Delete user %s (will also delete user from groups in the groups file)? y or n [y] ", userName); + fgets( (char*)&choice, 80, stdin); + if( choice[0] == 'n' || choice[0] == 'N' ) + exit(0); + } + + if (qtusersFilePath == NULL) + { + qtusersFilePath = new char[strlen(kDefaultQTPasswdFilePath)+1]; + strcpy(qtusersFilePath, kDefaultQTPasswdFilePath); + + } + + if (qtgroupsFilePath == NULL) + { + qtgroupsFilePath = new char[strlen(kDefaultQTGroupsFilePath)+1]; + strcpy(qtgroupsFilePath, kDefaultQTGroupsFilePath); + } + + if (realmString == NULL) + { + char* kDefaultRealmString = "Streaming Server"; + + realmString = new char[strlen(kDefaultRealmString)+1]; + strcpy(realmString, kDefaultRealmString); + } + + + tempUsersFile = NULL; + tempGroupsFile = NULL; + +#ifndef __Win32__ + signal(SIGINT, (void(*)(int))CleanupAndExit); + //create file with owner RW permissions only + umask(S_IRWXO|S_IRWXG); +#endif + + if (doCreateNewFile) + { + if (confirmPotentialDamage) + if( (usersFilePtr = fopen(qtusersFilePath, "r")) != NULL ) + { + closeFile(&usersFilePtr); + + qtss_printf("File already exists. Do you wish to overwrite it? y or n [y] "); + fgets( (char*)&choice, 80, stdin); + if( choice[0] == 'n' || choice[0] == 'N' ) + CleanupAndExit(); + + } + + //create new file or truncate existing one to 0 + if ( (usersFilePtr = fopen(qtusersFilePath, "w")) == NULL) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open password file %s for writing. (err=%d %s)\n", qtusersFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + perror("fopen"); + CleanupAndExit(); + } + + qtss_printf("Creating password file for realm %s.\n", realmString); + + //write the realm into the file + qtss_fprintf(usersFilePtr, "realm %s\n", realmString); + + closeFile(&usersFilePtr); + SetPrivileges(qtusersFilePath); + + + } + +#ifdef __Win32__ + char separator = '\\'; +#else + char separator = '/'; +#endif + char* tmpFile = "tmp.XXXXXX"; + char* alternateTempPath = "./tmp.XXXXXX"; + char* tempFilePath; + int tempFilePathLength = 0; + char* lastOccurOfSeparator = strrchr(qtusersFilePath, separator); + int pathLength = strlen(qtusersFilePath); + + if(lastOccurOfSeparator != NULL) + { + int filenameLength = ::strlen(lastOccurOfSeparator) + sizeof(char); + tempFilePathLength = pathLength - filenameLength + sizeof(char) + ::strlen(tmpFile) + 2; + + tempFilePath = new char[tempFilePathLength + 2]; + + memcpy(tempFilePath, qtusersFilePath, (pathLength - filenameLength + 2)); + memcpy(tempFilePath + (pathLength - filenameLength) + 2, tmpFile, ::strlen(tmpFile)); + tempFilePath[pathLength - filenameLength + ::strlen(tmpFile) + 2] = '\0'; + + /* Get temp users file path name */ + if (!createGroup && !deleteGroup) + tempUsersFile = GetTempFileAtPath(tempFilePath, tempFilePathLength); + + /* Get temp groups file path name */ + if ((groupName != NULL) || doDeleteUser) + tempGroupsFile = GetTempFileAtPath(tempFilePath, tempFilePathLength); + + delete [] tempFilePath; + } + else + { + if (!createGroup && !deleteGroup) + tempUsersFile = GetTempFileAtPath(alternateTempPath, ::strlen(alternateTempPath)); + if ((groupName != NULL) || doDeleteUser) + tempGroupsFile = GetTempFileAtPath(alternateTempPath, ::strlen(alternateTempPath)); + } + + if ((groupName != NULL) && !(groupsFilePtr = fopen(qtgroupsFilePath, "r"))) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open groups file %s to manipulate groups file. (err=%d:%s)\n", qtgroupsFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + + //create new file + if ( (groupsFilePtr = fopen(qtgroupsFilePath, "w")) == NULL) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not create a new groups file %s either. (err=%d %s)\n", qtgroupsFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + else + qtss_printf("Created new groups file %s.\n", qtgroupsFilePath); + + closeFile(&groupsFilePtr); + SetPrivileges(qtgroupsFilePath); + } + else + closeFile(&groupsFilePtr); + + if (createGroup) + { + AddOrDeleteGroup(1, groupName, qtgroupsFilePath, tempGroupsFile); + qtss_printf("Created new group %s\n", groupName); + return 0; + } + + if (deleteGroup) + { + AddOrDeleteGroup(0, groupName, qtgroupsFilePath, tempGroupsFile); + qtss_printf("Deleted group %s\n", groupName); + return 0; + } + + if (!(tempUsersFilePtr = fopen(tempUsersFile, "w"))) + { + char buffer[kErrorStrSize]; + qtss_printf("failed\n"); + qtss_fprintf(stderr, "Could not open temp users file. (err=%d %s)\n", errno, qtss_strerror(errno, buffer, sizeof(buffer))); + CleanupAndExit(); + } + + if (!(usersFilePtr = fopen(qtusersFilePath, "r"))) + { + char buffer[kErrorStrSize]; + qtss_fprintf(stderr, "Could not open passwd file %s for reading. (err=%d:%s)\n", qtusersFilePath, errno, qtss_strerror(errno, buffer, sizeof(buffer))); + qtss_fprintf(stderr, "Use -c option to create new one.\n"); + CleanupAndExit(); + } + + // Get the realm from the first line + while (!(GetLine(line, MAX_LINE_LEN, usersFilePtr))) + { + if ((line[0] == '#') || (!line[0])) + { + PutLine(tempUsersFilePtr, line); + continue; + } + else + { + // line is "realm somename" + if( strncmp(line, "realm", strlen("realm")) != 0 ) + { + qtss_fprintf(stderr, "Invalid users file.\n"); + qtss_fprintf(stderr, "The first non-comment non-blank line must be the realm line\n"); + qtss_fprintf(stderr, "The file may have been tampered manually!\n"); + CleanupAndExit(); + } + strcpy(realmFromFile ,line + strlen("realm")+1); + PutLine(tempUsersFilePtr, line); + break; + } + } + // Look for an existing entry with the username + found = 0; + while (!(GetLine(line, MAX_LINE_LEN, usersFilePtr))) + { + //write comments and blank lines out to temp file + if (found || (line[0] == '#') || (line[0] == 0)) + { + PutLine(tempUsersFilePtr, line); + continue; + } + strcpy(lineFromFile, line); + GetWord(usernameFromFile, lineFromFile, ':'); + + //if not the user we're looking for, write the line out to the temp file + if (strcmp(userName, usernameFromFile) != 0) + { + PutLine(tempUsersFilePtr, line); + continue; + } + else + { + if (doDeleteUser) + { //to delete a user - just don't write it out to the temp file + qtss_printf("Deleting user %s\n", userName); + //delete user from all groups in the group file + qtss_printf("Deleting user %s from all groups\n", userName); + AddOrDeleteUserFromGroup(0, userName, NULL, qtgroupsFilePath, tempGroupsFile); + + } + else + { + if (addUserToGroup) + { + PutLine(tempUsersFilePtr, line); + + qtss_printf("Adding user %s to group %s\n", userName, groupName); + AddOrDeleteUserFromGroup(1, userName, groupName, qtgroupsFilePath, tempGroupsFile); + } + else if (deleteUserFromGroup) + { + PutLine(tempUsersFilePtr, line); + + qtss_printf("Deleting user %s from group %s\n", userName, groupName); + AddOrDeleteUserFromGroup(0, userName, groupName, qtgroupsFilePath, tempGroupsFile); + } + else + { + qtss_printf("Changing password for user %s\n", userName); + if(password != NULL) + AddPasswordWithoutPrompt(userName, password, realmFromFile, tempUsersFilePtr); + else + AddPassword(userName, realmFromFile, tempUsersFilePtr); + } + } + found = 1; + } + } + + if (!found) + { + if (doDeleteUser) + { + qtss_printf("Username %s not found in users file.\n", userName); + + //delete user from all groups in the group file + qtss_printf("Deleting user %s from all groups if found in groups file\n", userName); + AddOrDeleteUserFromGroup(0, userName, NULL, qtgroupsFilePath, tempGroupsFile); + CleanupAndExit(); + } + + /* check for : in name before adding user */ + if(strchr(userName, ':') != NULL) + { + qtss_printf("Username cannot contain a ':' character."); + CleanupAndExit(); + } + + qtss_printf("Adding userName %s\n", userName); + if(password != NULL) + AddPasswordWithoutPrompt(userName, password, realmFromFile, tempUsersFilePtr); + else + AddPassword(userName, realmFromFile, tempUsersFilePtr); + + if (addUserToGroup) + { + qtss_printf("Adding user %s to group %s\n", userName, groupName); + AddOrDeleteUserFromGroup(1, userName, groupName, qtgroupsFilePath, tempGroupsFile); + } + else if (deleteUserFromGroup) + { + qtss_printf("Deleting user %s from group %s\n", userName, groupName); + AddOrDeleteUserFromGroup(0, userName, groupName, qtgroupsFilePath, tempGroupsFile); + } + } + + closeFile(&usersFilePtr); + closeFile(&tempUsersFilePtr); + + // Remove old file and change name of temp file to match new file + remove(qtusersFilePath); + + result = rename(tempUsersFile, qtusersFilePath); + if(result != 0) + { + perror("rename failed with error"); + CleanupAndExit(); + } + + SetPrivileges(qtusersFilePath); + CleanUp(); + return 0; +} + + +void SetPrivileges(char *filePath) +{ +#ifndef __Win32__ + + int result =0; + uid_t owner = (uid_t) -1; // if fileOwner not found set to ignore owner setting + + struct passwd* pw = ::getpwnam(fileOwner); // set by -O at the command line + if (pw) + owner = pw->pw_uid; + + +#if __MacOSX__ + if (owner == (uid_t) -1) // force user 76 on OS X. + owner = 76; + + result = ::chown(filePath,owner,80);//default is owner qtss, group admin + //printf("chown %s result =%d errno=%d\n",filePath, result, errno); + + if (result != 0) + { + qtss_fprintf(stderr, "permission failure accessing file %s\n", filePath); + CleanupAndExit(); + } +#endif + + result = ::chown(filePath, (uid_t) owner, (gid_t) -1);//default is owner qtss, group root + + if (result != 0) + result = ::chmod(filePath, S_IRUSR | S_IWUSR | S_IRGRP ); + + //printf("chmod %s result =%d errno=%d\n",filePath, result,errno); + + if (result != 0) + { qtss_fprintf(stderr, "permission failure accessing file %s\n", filePath); + CleanupAndExit(); + } +#endif + + +}