Go Premium for a chance to win a PS4. Enter to Win


Attempting to run su using pipes, dup2, execl from a c++ app

Posted on 2004-08-23
Medium Priority
Last Modified: 2008-01-09
OK here is what my app is actually doing.  I have a c++ application that basically receives input from a php page.  That part is coded perfectly fine,  I can send info from the page, and receive it from the app, as well as the app send info to the page.

Here is my problem.  I want the user to be able to type in their name and password, this name and password also is the same as the username and password they have on the linux box (RH9).  Originally I was going to try to compare the encrypted password in the shadow file to what the user types in, but I don't see that as possible after doing some research.

Is there an easy way to do this?  Check to see if a password matches that for a username?

My other solution was the following:
NOTE: the application will not be running as root

I could have a c++ app run "su username_entered_from_web_page_here"
then have the c++ app type in the corresponding password I received from the page.
The function below will return true if the login is executed correctly, and false otherwise.
If the user logs in correctly I would also have the ability to execute some more commands.

Assuming I have the username in a string username, and the password in a string password, how can I accomplish this?

Exact code would be GREAT, here is the code I have so far:

bool verifyUser(string user, string pass){
      cout << "Starting verification process\n";

      int filedes1[2], filedes2[2];
      int pid;
      /* Make our first pipe. */

      if (pipe(filedes1) == -1){
            perror ("pipe");
            return false;

      /* Make our second pipe. */

      if (pipe(filedes2) == -1){
            perror ("pipe");
            return false;

      /* Fork a child */

      if ((pid = fork()) == 0){
            dup2(filedes1[0], fileno(stdin)); /* Copy the reading end of the pipe. */
            dup2(filedes2[1], fileno(stdout)); /* Copy the writing end of the pipe */

      /* Uncomment this if you want stderr sent too.

      dup2(filedes2[1], fileno(stderr));


      /* If execl() returns at all, there was an error. */

            if (execl("/bin/su","su",user.c_str(),NULL)){
                  return false;
      else if (pid == -1){
            return false;
            int output;
            char c;

            write(filedes1[1], pass.c_str(), strlen(pass.c_str())); /* Write the string */

            int outputLength;
            char buffer[2048];
            outputLength = read(filedes2[0], buffer, 2048);
            string s(buffer,outputLength);

            //cout << s << "\n";
            //cout << "Done reading\n";
            return false;

and of course this does not work, please help
Pretty much anyway I've tried it I tend to get this error:
"standard in must be a tty"
Question by:rhedrick
  • 4
  • 3
LVL 23

Expert Comment

ID: 11878235
Su doesn't read the password from stdin, after it's launched it opens up /dev/tty and grabs the input directly from
the terminal device.  You could try and automate a remote login to the local system (then what su sees as /dev/tty
will be login program's stdin),

but no amount of re-mapping fds 0, 1, or 2  will convince su to allow the password to just be piped in....

In general, the program needs to be running as root to check the password for a local user.
(Programs running with ordinary user privs don't have access to the encrypted passwords)

I'll try to think of some other method of getting SU to do what you want and check back soon

Author Comment

ID: 11878486
well i can login as root, that wouldn't be a problem

so you're saying that if I log in as root, I can verify the users password?  I thought I could only change it.

LVL 22

Accepted Solution

grg99 earned 340 total points
ID: 11879988
Just run something like a POP2 or POP3 or IMAP or FTP server.  They all have a "User name pw" command that will validate logins for you.

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.


Author Comment

ID: 11884832
yes well i don't want to run a pop2, pop3, imap or ftp server.  I need to be able to drop this c++ app on any of the RH9 servers and it will run no matter what.  Some of them have FTP, but that is it.

LVL 23

Expert Comment

ID: 11897622
You either want to authenticate them using PAM; or if you want to make assumptions
about the authentication method, use the shadow functions

getspnam(user)  to read their entry from the shadow file
then compare it using crypt.

For details about using pam

See the Linux-Pam Application Developer's Guide, section 2

And the example code: http://www.krasline.ru/Library/pam-doc/pam_appl-6.html

for an example PAM app; it may be sufficient to replace  misc_conv with a conversation
function you would write according to that documentation

You might find these snippets in addition to the example code in the docs
useful for experimenting:

// gcc -o progname progname.c -lpam
// to compile,  requires pam library headers.. may be in a separate
// libpam-devel or libpam0-devel  .. not sure about RH9

// This is code is a conversation function that could be used with the example code in the
// Linux Pam Application Developer's Guide... this isn't standalone code, only a conversation
// function

#include <stdlib.h>
int get_line(char *buf, int size)
        if (!fgets(buf, size, stdin))
                return 0;
        if (buf[0] == '\n' || buf[0] == '\0')
                return 0;
        buf[strlen(buf) - 1] = '\0';
        return 1;

int my_convo(int num_msg, const struct pam_message**msg,
             struct pam_response** resp, void *appdata_ptr)
        struct pam_response *myresp = calloc(num_msg, sizeof(struct pam_response));
        char input[256];
        int i;
        for(i = 0; i < num_msg; i++) {
                if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
                        printf("%s", msg[i]->msg);
                        if (get_line(input, 256)) {
                                myresp[i].resp = strdup(input);
                } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
                        /* Warning: this basically ignores the echo off prompting and leaves echo on */
                        printf("%s", msg[i]->msg);
                        if (get_line(input, 256)) {
                                myresp[i].resp = strdup(input);
                } else if (msg[i]->msg_style == PAM_ERROR_MSG) {
                        printf("%s\n", msg[i]->msg);
                } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
                        printf("%s\n", msg[i]->msg);
        *resp = myresp;
        return PAM_SUCCESS;
} // Warning.. this leaves myresp[] allocated and various myresp[ items ]->resp allocated

Author Comment

ID: 11899338
OK I tried the example on the second link you posted, and I think this is exactly what I want... but I'm getting this error when I'm compiling:

[root@localhost root]# g++ test2.cpp -o test2
In file included from /usr/include/security/pam_misc.h:7,
                 from test2.cpp:12:
/usr/include/security/pam_client.h:55: non-local function `int
   pamc_converse(pamc_handle_s*, <anonymous struct>**)' uses anonymous type

Sorry i'm not 100% new to compiling on linux, but I'm not an expert, what is an anonymous type?
LVL 23

Assisted Solution

Mysidia earned 340 total points
ID: 11928141
Anonymous type is how pamc_bp_t is declared.  

Mostly it just means there's no "struct name"

It's ok in C, which the pam libraries are written in, but apparently your system's C++
compiler doesn't like it.

Try placing the two pam #includes in test2.cpp inside a C linkage declaration:

extern "C" {
#include <security/pam_appl.h>
#include <security/pam_misc.h>

and when compiling do
g++ test2.cpp -o test2 -lpam

Author Comment

ID: 12322866
I am still trying to get this to work, I do attend on awarding the points when I get it working correctly =P

Actual work seems to get in the way when I start working on this

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
Suggested Courses

926 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question