Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2552
  • Last Modified:

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

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
rhedrick
Asked:
rhedrick
  • 4
  • 3
2 Solutions
 
MysidiaCommented:
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
 
rhedrickAuthor Commented:
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
 
grg99Commented:
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
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.

 
rhedrickAuthor Commented:
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
 
MysidiaCommented:
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
 
rhedrickAuthor Commented:
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
 
MysidiaCommented:
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
 
rhedrickAuthor Commented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now