LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-03-2011, 05:42 PM   #1
briggsrmb
LQ Newbie
 
Registered: Feb 2011
Posts: 3

Rep: Reputation: 0
Question C wrapper for secure password substitution - Compilation issues


Hi all, I'm working on a program that will allow non-privileged users the ability to substitute passwords that are read in from a 600-root:root file.

A user might call it like this:
/bin/securepasswrapper "/path/to/check_ping -u Admin -p ?webserver1?"

The file (defined now as /tmp/securefile) will be searched for the identifier "webserver1", and it's corresponding password will be returned to securepasswrapper. Now, I know you could just call /bin/echo, and get the output, but that's in scope at this point.

File format of securefile is:
<identifierassword>
ex: webserver1:asdf123

Once the password is properly substituted in, securepasswrapper should then execute it as the UID/EUID of the calling user (not root)...that part I've not gotten to yet. Hell, I'd just be happy if it echo'd out the proper command at this point.

Any assistance would be hugely appreciated

Code:
/* program: securepasswrapper
   author: R.Briggs + Google
   date: 02/02/11

   purpose: C wrapper that replaces placeholders in a string with secure passwords retrieved from a file
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define SECUREFILE '/tmp/securefile' // this file is owned by root, and mode 600

char initialcommand[1024]; // = 'check_ping -p ?mailserver? -p ?webserver?'

int getPlaceholders()
{
	int in_placeholder = 0; // indicate if we are inside a possible placeholder
	int pos = 0;
	int placepos = 0;
	char placeholder[1024];
	char newcommand[1024]; // what we will return after substituting placeholders
	
	for(pos=0; pos < (signed int)strlen(initialcommand); pos++)
	{
		if(in_placeholder)
		{
			// if continue placeholder
			if (
			/*
			REFACTOR
			(initialcommand[pos] >= '0' && initialcommand[pos] <= '9') ||
			(initialcommand[pos] >= 'A' && initialcommand[pos] <= 'Z') ||
			(initialcommand[pos] >= 'a' && initialcommand[pos] <= 'z') ||
			initialcommand[pos] == '_'
			*/
			isDigit(initialcommand[pos]) ||
			isAlpha(initialcommand[pos]) ||
			initialcommand[pos] == '_'
			)
			{
				placeholder[placepos] = initialcommand[pos];
				placepos++;
			}
			// else, end place holder
			else
			{
				// valid terminated placeholder, interpolate
				if(initialcommand[pos] == '?')
				{
					// WE HAVE A MATCH!
					// now let's do something with it...
					placeholder[placepos] = '\0';
					printf("matched placeholder '%s' at position %d\n", placeholder, pos);
					// getSecurePass(placeholder); // 'webserver'
					// take getSecurePass return and then call getReplace with '?mailserver?,return[pw]'
					// getReplace should return initialcommand with substitution done for round 1
					// perform additional rounds as necessary
					// at some point we will finish finding all of the placeholders, return 'newcommand'
					//getReplace(placeholder, getSecurePass(placeholder));
					// at this point we should have one round of our initialcommand updated 
					strcpy(initialcommand, getReplace(placeholder, getSecurePass(placeholder)));
				}
				// cleanup
				in_placeholder = 0;
				placeholder[0] = '\0';
				placepos=0;
			}
		}
		else
		{
			if(initialcommand[pos] == '?') // entering a potential placeholder
			{
				in_placeholder = 1;
				placepos = 0;
			}
		}
	}
	// ok, now we're done fixing up the initialcommand with its replacements, now lets copy that to newcommand and return it
	return(0);
}

char *getSecurePass(char *placeholder)
{
	FILE *fp;
	int find_result = 0;
	int templine_len = 0;
	char templine[512];
	char password[512];
	char *token;
	
	
	if((fp = fopen(SECUREFILE, 'r')) == NULL)
	{
		return(-1);
	}
	
	while(fgets(templine, 512, fp) != NULL)
	{
		if((strstr(templine, placeholder)) != NULL)
		{
			templine_len = strlen(templine);
			if(templine[templine_len-1] == '\n' )
			{
				templine[templine_len-1] = '\0';
			}
			token = strtok(templine, ':');
			token = strtok(NULL, ':');
			strcpy(password,token);
			find_result++;
		}
	}
	if(find_result == 0) 
	{
		printf("No password found for placeholder: %s\n", placeholder);
		exit(1);
	}
	//Close the file if still open.
	if(fp)
	{
		fclose(fp);
	}
	return password;
}

char *getReplace(char *placeholder, char *password)
{
	char buffer[4096];
	char *p;
	
	if(!(p = strstr(initialcommand, placeholder)))  // Is 'placeholder' even in 'initialcommand'?
		return initialcommand;
	
	strncpy(buffer, initialcommand, p-initialcommand); // Copy characters from 'initialcommand' start to 'placeholder'
	buffer[p-initialcommand] = '\0';

	sprintf(buffer+(p-initialcommand), "%s%s", password, p+strlen(placeholder));

	return buffer;
}

int main(int argc, char *argv[])
{
	if(argc < 2 || argc > 2)
	{
		printf("Usage: %s <command>\n", argv[0]);
		exit(1);
	}
	
	strcpy(initialcommand, argv[1]);
	
	getPlaceholders();
	
	printf("FINAL COMMAND: %s\n", initialcommand);
	
	return(0);
}
Right now, there are compilation errors all over the place...

[root@staff1 tmp]# cc getPass.c -o getPass
getPass.c: In function ‘getPlaceholders’:
getPass.c:62: warning: passing argument 2 of ‘strcpy’ makes pointer from integer without a cast
getPass.c: At top level:
getPass.c:84: error: conflicting types for ‘getSecurePass’
getPass.c:62: error: previous implicit declaration of ‘getSecurePass’ was here
getPass.c:93:17: warning: character constant too long for its type
getPass.c: In function ‘getSecurePass’:
getPass.c:93: warning: passing argument 1 of ‘fopen’ makes pointer from integer without a cast
getPass.c:93: warning: passing argument 2 of ‘fopen’ makes pointer from integer without a cast
getPass.c:95: warning: return makes pointer from integer without a cast
getPass.c:107: warning: passing argument 2 of ‘strtok’ makes pointer from integer without a cast
getPass.c:108: warning: passing argument 2 of ‘strtok’ makes pointer from integer without a cast
getPass.c:123: warning: function returns address of local variable
getPass.c: At top level:
getPass.c:127: error: conflicting types for ‘getReplace’
getPass.c:62: error: previous implicit declaration of ‘getReplace’ was here
getPass.c: In function ‘getReplace’:
getPass.c:139: warning: function returns address of local variable
[root@staff1 tmp]#
 
Old 02-03-2011, 06:10 PM   #2
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 374Reputation: 374Reputation: 374Reputation: 374
I haven't looked at everything completely thoroughly yet, but i think I can help with the first one:
Quote:
[root@staff1 tmp]# cc getPass.c -o getPass
getPass.c: In function ‘getPlaceholders’:
getPass.c:62: warning: passing argument 2 of ‘strcpy’ makes pointer from integer without a cast
getPass.c: At top level:
getPass.c:84: error: conflicting types for ‘getSecurePass’
getPass.c:62: error: previous implicit declaration of ‘getSecurePass’ was here
I think the problem with this is that you're using getSecurePass() before you declare it. The compiler defaults to "int" arguments for undeclared parameters. You should be able to fix this by giving a prototype for getSecurePass() at the top of the file.

EDIT:
Quote:
getPass.c:93:17: warning: character constant too long for its type
getPass.c: In function ‘getSecurePass’:
getPass.c:93: warning: passing argument 1 of ‘fopen’ makes pointer from integer without a cast
getPass.c:93: warning: passing argument 2 of ‘fopen’ makes pointer from integer without a cast
These are related. You define SECUREFILE like this:
Code:
#define SECUREFILE '/tmp/securefile' // this file is owned by root, and mode 600
And reference it here:
Code:
	if((fp = fopen(SECUREFILE, 'r')) == NULL)
In C, you use double quotes (") to specify a char * (or string) literal. You use single quotes (') to specify a char literal. So your #define uses the wrong quotes.

Because char literals are replaced with their ASCII/Unicode values, your reference to SECUREFILE in fopen() uses an ASCII/Unicode value for the first argument; and that argument should be a char *. Again this error will be fixed if you change the quotes in your #define.

Similarly, the second argument to fopen() requires a char *. And again, the single quotes around the r are the wrong type of quotes to use. Switch them to double quotes and that will fix the problem.


Next up:
Quote:
getPass.c:95: warning: return makes pointer from integer without a cast
The offending line is:
Code:
		return(-1);
Your function returns a char * not an int. Typically, the way to signal an error in a function that returns a pointer of any sort is to set the pointer to NULL. The code that calls this function will then need to check if the returned value is NULL before dereferencing the pointer.


Next up:
Quote:
getPass.c:107: warning: passing argument 2 of ‘strtok’ makes pointer from integer without a cast
getPass.c:108: warning: passing argument 2 of ‘strtok’ makes pointer from integer without a cast
This is the same problem as the #define and fopen() issue above: the wrong quotation marks used for the second argument to strok().



Next up:
Quote:
getPass.c:123: warning: function returns address of local variable
And here's the line:
Code:
	return password;
You should not return references to variables created locally for the function. I'm not 100% sure about this, but I believe that the memory used for local variables can be rewritten, wiped out, or otherwise mangled at any moment after a function ends.

The typical way that I handle this is to have the function reserve memory using malloc() and return that chunk of memory. Of course, the code that calls this kind of function must remember to free the memory later on.



Next up:
Quote:
getPass.c: At top level:
getPass.c:127: error: conflicting types for ‘getReplace’
getPass.c:62: error: previous implicit declaration of ‘getReplace’ was here
This is related to my original comment. The compiler guessed what the definition of getReplace() should be because it was used in the code before it was defined. A function prototype will fix this.



Next up:
Quote:
getPass.c: In function ‘getReplace’:
getPass.c:139: warning: function returns address of local variable
This is the same problem as mentioned earlier--the content of a functions local variable is undefined once the program leaves the function.

Last edited by Dark_Helmet; 02-03-2011 at 08:12 PM.
 
1 members found this post helpful.
Old 02-03-2011, 11:24 PM   #3
briggsrmb
LQ Newbie
 
Registered: Feb 2011
Posts: 3

Original Poster
Rep: Reputation: 0
Thanks for the pointers Good to know the quoting rules too.
 
Old 02-04-2011, 04:20 AM   #4
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,541

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Btw, your getPlaceholders() seems "risky" by using fixed-sized arrays for the commands. I don't see anywhere where the substitution is taking place (maybe because I did not have the patience to study the code too much), but it seems that one could thwart your application by creating a buffer-overrun.

Here's another way to implement getPlaceholders(), which I believe would be better if named replacePlaceholders():
Code:
char* replacePlaceholder(char* cmd)
{
   char* firstMarker = strchr(cmd, '?');
   char* newcmd      = NULL;

   if (firstMarker)
   {
      char* secondMarker = strchr(firstMarker + 1, '?');

      if (secondMarker)
      {
         char* placeholder = firstMarker + 1;

         *firstMarker  = '\0';
         *secondMarker = '\0';

         if (placeholder)
         {
            const char* password = getPassword(placeholder);

            if (password)
            {
               newcmd = strdup(cmd);
               newcmd = realloc(newcmd, strlen(newcmd) + strlen(password) + 1);

               strcat(newcmd, password);
            }
            else
            {
               fprintf(stderr, "Password not found for placeholder.\n");
            }
         }
         else
         {
            fprintf(stderr, "Missing placeholder; need '?placeholder?'\n");
         }

         /* if there is more info past our second marker, then add it */
         if (newcmd && *(secondMarker + 1) != '\0')
         {
            newcmd = realloc(newcmd, strlen(newcmd) + strlen(secondMarker + 1) + 1);
            strcat(newcmd, secondMarker + 1);
         }
      }
      else
      {
         fprintf(stderr, "End marker not found.\n");
      }
   }
   else
   {
      fprintf(stderr, "Begin marker not found.\n");
   }

   return newcmd;
}
As for your getSecurePass(), I believe it would be safe and prudent to exit the while-loop after you have located the placeholder entry in the file. Surely you do not have duplicate entries in the file, and thus it is not necessary to continue reading the file once you have the information you require.


P.S. I realize that my replacePlaceholder() is also not bullet-proof, for if one places the placeholder in the middle of the command string, then the remainder of the string would be lost after the password replacement. Maybe I should put more thought into that.

Last edited by dwhitney67; 02-05-2011 at 06:01 AM. Reason: updated code
 
Old 02-04-2011, 02:45 PM   #5
briggsrmb
LQ Newbie
 
Registered: Feb 2011
Posts: 3

Original Poster
Rep: Reputation: 0
Ah yes, buffer overruns

Here's the updated code so far...everything compiles cleanly, but when I tried to integrate your new replacePassword() function, some things didn't line up right.

Code:
/* program: getPass.c
   author: R.Briggs + Google
   date: 02/02/11
   version: 0.3

   purpose: C wrapper that replaces delimited placeholders in a string with secure passwords retrieved from a file
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// this file should be owned by root and mode 600
#define SECUREFILE "/tmp/securefile"

char initialcommand[1024]; // = "check_database -u monitor -p ?dbserver1?"
char password[1024];
char buffer[1024];

// function to get the actual password from SECUREFILE as root
// file format is <placeholder>:<realpassword>
char *getSecurePass(char *placeholder)
{
	FILE *fp;
	int find_result = 0;
	int templine_len = 0;
	char templine[1024];
	char* token;
	
	// open the file, if error return NULL
	if((fp = fopen(SECUREFILE, "r")) == NULL)
	{
		printf("Unable to open SECUREFILE.\n");
		exit(1);
	}
	
	// take in each line of SECUREFILE
	while(fgets(templine, 1024, fp) != NULL)
	{
		// if the placeholder matches
		if((strstr(templine, placeholder)) != NULL)
		{
			templine_len = strlen(templine);
			// remove any \n's from the fgets() call
			if(templine[templine_len-1] == '\n' )
			{
				// null terminate the string
				templine[templine_len-1] = '\0';
			}
			// split on :
			token = strtok(templine, ":");
			// read in the real password
			token = strtok(NULL, ":");
			strcpy(password,token);
			// set find_result to 1 so that we glide past the next if
			find_result++;
		}
	}
	// if the placeholder doesnt match, exit
	if(find_result == 0)
	{
		printf("No password found for placeholder: %s\n", placeholder);
		exit(1);
	}
	// close the file
	if(fp)
	{
		fclose(fp);
	}
	return password;
}

// this function is used to replace the placeholder with the real password
char *getReplace(char *placeholder, char *password)
{
	char *p;
	
	// is the placeholder even in initialcommand?
	if(!(p = strstr(initialcommand, placeholder)))
	{
		return initialcommand;
	}
	
	// copy characters from start of 'initialcommand' until 'placeholder'
	strncpy(buffer, initialcommand, p-initialcommand);
	buffer[p-initialcommand] = '\0';

	// concat it all back together and get rid of the "?" delimiters at the same time
	sprintf(buffer+(p-initialcommand-1), "%s%s", password, p+strlen(placeholder)+1);

	return buffer;
}

int getPasswords()
{
	int in_placeholder = 0; // indicate if we are inside a possible placeholder
	int pos = 0;
	int placepos = 0;
	char placeholder[1024];
	
	for(pos=0; pos < (signed int)strlen(initialcommand); pos++)
	{
		if(in_placeholder)
		{
			// if continue placeholder
			if (
			isdigit(initialcommand[pos]) ||
			isalpha(initialcommand[pos]) ||
			initialcommand[pos] == '_'
			)
			{
				placeholder[placepos] = initialcommand[pos];
				placepos++;
			}
			// else, end place holder
			else
			{
				// valid terminated placeholder, we have a match, now interpolate
				if(initialcommand[pos] == '?')
				{
					placeholder[placepos] = '\0';
					strcpy(initialcommand, getReplace(placeholder, getSecurePass(placeholder)));
					// TODO:NEED TO HANDLE THE POSSIBLE NULL FROM GETSECUREPASS BETTER
				}
				// cleanup
				in_placeholder = 0;
				placeholder[0] = '\0';
				placepos=0;
			}
		}
		else
		{
			// entering a possible placeholder
			if(initialcommand[pos] == '?')
			{
				// first, check to make sure we didnt enter an empty placeholder
				if(initialcommand[pos] == '?' && initialcommand[pos+1] == '?')
				{
					printf("Unbalanced or empty delimiters detected in string\n");
					exit(1);
				}
				// set in_placeholder to 1.  next we need to find the terminating "?"
				in_placeholder = 1;
				placepos = 0;
			}
		}
	}
	return(0);
}

int main(int argc, char *argv[])
{
	if(argc < 2 || argc > 2)
	{
		printf("Usage: %s <command>\n", argv[0]);
		exit(1);
	}
	
	// copy argv[1] to our initialcommand char
	strcpy(initialcommand, argv[1]);
	
	// pull the placeholders out, look up the real passwords, and substitute them back in
	getPasswords();
	
	// reset the euid to that of the calling user
	seteuid(getuid());
	setgid(getgid());
	// call whatever was passed into argv[1], replacing the placeholder(s) with the real password(s)
	system(initialcommand);
	
	return(0);
}
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
secure login password shaveta Linux - Security 1 01-20-2011 12:25 AM
[SOLVED] how to secure folders and files with password senthil1186 Red Hat 2 10-01-2010 07:51 AM
LXer: Choosing a Secure Password LXer Syndicated Linux News 0 01-05-2009 08:50 PM
Secure Password Management win32sux General 0 04-30-2005 08:11 AM
Secure Password Authenication danielrcummins Linux - Software 2 03-26-2003 09:05 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 12:24 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration