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

Posted on 2004-08-23
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 85 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.

Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center


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:

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 85 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

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

840 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