Solved

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

Posted on 2004-08-23
11
2,501 Views
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)){
                  perror("execl");
                  return false;
            }
      }
      else if (pid == -1){
            perror("fork");
            return false;
      }
      else{
            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"
0
Comment
Question by:rhedrick
  • 4
  • 3
11 Comments
 
LVL 23

Expert Comment

by:Mysidia
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
0
 

Author Comment

by:rhedrick
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.

Josh
0
 
LVL 22

Accepted Solution

by:
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.

0
 

Author Comment

by:rhedrick
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.

Josh
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 23

Expert Comment

by:Mysidia
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
 http://www.krasline.ru/Library/pam-doc/pam_appl-2.html

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
0
 

Author Comment

by:rhedrick
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?
0
 
LVL 23

Assisted Solution

by:Mysidia
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:
i.e.

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

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

Author Comment

by:rhedrick
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
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

746 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now