C wrapper for secure password substitution - Compilation issues
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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]#
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.
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
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);
}
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.