Re: Face recognition in GDM ...



Sure, sample code attached, with no warranties or guarantees :-)
It's very crude but it works.  I should apologize - I see it's
180 lines, not 30 as IIRC'd :-).  But it's mostly printfs, as
you can see, and no actual code complexity to speak of
(other than exploring all the obscure PAM message types
and returns).

Note we had the interaction model wrong.  What you want
to write is an authentication service, not a client. Input to it is probably
just an implicit "Can the user be authenticated?" (and an optional
username - see below), and output is simply "yes/no" and a username.
In fact, you may prefer that it never return until it successfully
authenticates somebody (i.e. keep looping until you can return "yes"
for somebody).

The main point here is that all the GUI interaction, widgets, prompts,
etc belong in the authentication service only, so all the stuff we
were debating about the interface to the GUI is really misdirected.
It would be plain silly to generate the prompts from PAM (which *can*
be used to generate prompts in the PAM client (e.g. gdm), but that's
not what you're doing), or from gdm (unless you wanted to tightly
integrate with the gdm GUI - i.e. add your own widgets and put the
camera view directly into the greeter window, but that's a lot of gdm
work that's not relevant to the thesis research).  That's good news
for you - it keeps all your work in one place, making it easier to
test and develop.  If you develop it as a separate app and use IPC to
talk to it you can isolate the greeter from core dumps :-).  I'd
recommend using a simple IPC like pipes or named pipes and a simple
ASCII protocol to talk to it, for easy debugging (you could test it
simply by echo into a named pipe and cat out of it).

The only real question for you is whether you want the GUI to take a
username, so you can try to match a particular user, or whether you
want to try to distinguish the user from all possible face data (that
doesn't seem very secure to me, but might be interesting from a
recognition-research POV).  In the end, you'll come up with a
username, either because it was typed in or your recognition subsystem
came up with it.  You will need to return this from your
authentication application.  In your PAM service module you'll want to
use that string to set PAM_USER, using a call to pam_set_item().  If
you really wanted to be PAM-friendly you'd pass in any preset value
for PAM_USER, in case some prior PAM module wanted to come up
with the username in some clever way (e.g. a smartcard).  If it's not
preset, your GUI should acquire it somehow and return it.

Hope this is enough.  You'll need to write your own PAM
service module but it should be simple.  Good luck.

-Bob

José Miguel Buenaposada Biencinto wrote:

Thanks a lot both ...

I like to do the things in the right way. If PAM is tha place to do
that we'll go for it. Bob, could you send to me this example GUI
code for PAM? I will be very happy with that.

Best regards to all.
JM.

Bob Doolittle wrote:

Anybody can very easily write a GUI to accomplish the goal here with
PAM.  You could put up a GUI displaying the current face, with a
prompt under it for "Press enter to start recognition".  When enter is
pressed, the PAM module could use any number of IPC mechanisms
(like a two-way pipe) to signal the GUI to acquire the frame and pass
back status.  Maybe you can write prompt strings to the GUI on a pipe
and it can write back status.

As mentioned before, this solution would apply to screen locks as
well as GDM.  Wouldn't you like to unlock your screen this way also?

But the bigest benefit from a masters thesis POV is the simplicity
resulting from the isolation.  It's simple to write a PAM client to
test the GUI out (I'll send you mine if you like, it's about 30 lines
of code IIRC), letting you focus on the GUI and recognition instead of
learning the internals of gdm.  When you want gdm to use it it's
trivial to edit /etc/pam.conf to put the service module on the stack,
as well as the stack of your favorite screen-lock utility.

I'm sorry George, but there's just no contest here.  PAM is simpler
and more flexible for this particular application.  Authentication is
what it's designed for.  I'll agree that for a more complex GUI PAM
has shortcomings (I'm in the middle of that right now for a more
complex GUI I'm having to PAM-ify), but this seems simple
enough to provide a good fit.

-Bob

George wrote:

On Tue, Sep 07, 2004 at 01:02:16PM +0200, José Miguel Buenaposada Biencinto wrote:
snipped

As was pointed out, the somewhat correct blue sky way of doing things would be PAM, with the caveat that PAM would kind of suck for this (PAM sucks in many ways, but it's better then the alternative since there are really no
alternatives).  PAM is a question and answer kind of system which is
synchroneous in nature. So really the best you could do is that the pam module would say something like: "Press enter to start recognition" and then gdm would wait for enter, then the PAM code could figure out the username and
such ...

I think way too much whackiness for a masters thesis ... I think for a
prototype it would be best to just modify gui/gdmlogin.c which is the
standard greeter. I suspect most code will be the face recognition and this would be the absolutely simplest way of getting it working quickly. Later when someone is in a masochistic mood, that person should attempt to write a PAM module and use that code. Really that's just an implementation detail.

So in summary, PAM is the correct place to do this except that PAM was
invented by people that hated GUIs and GUI programmers in general and wanted to make their life as miserable as possible. That is my current theory.

George





#if 0
set -x
cc -g testpamclient.c -lpam -o testpamclient
exit 0
#endif

#include <sys/types.h>
#include <security/pam_appl.h>
#include <stdio.h>

struct appdata {
    char *string;
};

char *pam_error(int errcode);

int bogopam_callback(int num_msg, struct pam_message **msg,
		     struct pam_response **resp, void *appdata_ptr);
void do_bogopamauth(pam_handle_t *pamh);

main(int argc, char *argv[])
{
    struct pam_conv pam_conv;
    pam_handle_t *pamh = NULL;
    int retcode;
    char *service = "BogoPAM";
    char *user = (argc == 2) ? argv[1] : NULL;
    struct appdata appdata = { "This is the app data" };

    if (argc != 1 && argc != 2) {
	fprintf(stderr, "Usage: testpamclient <userid>\n");
	exit(1);
    }

    pam_conv.appdata_ptr = &appdata;
    pam_conv.conv = (int (*)())bogopam_callback;
    retcode = pam_start(service, user, &pam_conv, &pamh);
    if (retcode != 0) {
	fprintf(stderr, "pam_start returned %d\n", retcode);
	exit(2);
    }

    do_bogopamauth(pamh);
    
    retcode = pam_end(pamh, 0);
    if (retcode != 0) {
	fprintf(stderr, "pam_end returned %d\n", retcode);
	exit(2);
    }
    exit(0);
}

int bogopam_callback(int num_msg, struct pam_message **msg,
		     struct pam_response **resp, void *appdata_ptr)
{
    int i;
    struct pam_message *pm;
    char inl[80];
    
    printf("The bogopam_callback has been invoked:\n");
    printf("\tnum_msg:\t%d\n", num_msg);
    for (i = 0, pm = *msg; i < num_msg; ++i, ++pm) {
	printf("\tpam_message:\n");
	
	printf("\t\tmsg_style:\t");
	switch (pm->msg_style) {
	  case PAM_PROMPT_ECHO_OFF:
	    printf("PAM_PROMPT_ECHO_OFF");
	    break;
	  case PAM_PROMPT_ECHO_ON:
	    printf("PAM_PROMPT_ECHO_ON");
	    break;
	  case PAM_ERROR_MSG:
	    printf("PAM_ERROR_MSG");
	    break;
	  case PAM_TEXT_INFO:
	    printf("PAM_TEXT_INFO");
	    break;
#ifdef PAM_MSG_NOCONF
	  case PAM_MSG_NOCONF:
	    printf("PAM_MSG_NOCONF");
	    break;
#endif
#ifdef PAM_CONV_INTERRUPT
	  case PAM_CONV_INTERRUPT:
	    printf("PAM_CONV_INTERRUPT");
	    break;
#endif
	  default:
	    printf("Unknown msg_style %d\n", pm->msg_style);
	    break;
	}
	printf("\n");
	
	printf("\t\tmsg:\t'%s'\n", pm->msg);
	printf("\n");

    }
    printf("\tappdata:\t'%s'\n", ((struct appdata *)appdata_ptr)->string);

    *resp = (struct pam_response *)calloc(1, sizeof(struct pam_response));
    if (resp == NULL) {
	printf("ENOMEM on calloc!!!\n");
	exit(2);
    }
    printf("Callback response value? ");
    fflush(stdout);
    fgets(inl, sizeof(inl), stdin);
    (*resp)->resp = (char *) strdup(inl);

    printf("Callback return value? ");
    fflush(stdout);
    fgets(inl, sizeof(inl), stdin);
    return(atoi(inl));
}

void
do_bogopamauth(pam_handle_t *pamh)
{
    int flags = 0;
    int retcode;
    char inl[80];

    printf("pam_authenticate flags: PAM_SILENT (y/n)? ");
    fflush(stdout);
    fgets(inl, sizeof(inl), stdin);
    if (*inl == 'y') {
	flags |= PAM_SILENT;
    }

    printf("pam_authenticate flags: PAM_DISALLOW_NULL_AUTHTOK (y/n)? ");
    fflush(stdout);
    fgets(inl, sizeof(inl), stdin);
    if (*inl == 'y') {
	flags |= PAM_DISALLOW_NULL_AUTHTOK;
    }

    retcode = pam_authenticate(pamh, flags);
    if (retcode != PAM_SUCCESS) {
	fprintf(stderr, "pam_authenticate returned %s\n",
		pam_error(retcode));
	exit(2);
    }
    printf("Authentication successful!\n");
}

char *
pam_error(int errcode)
{
    static char retbuf[80];
    
    switch(errcode) {
      case PAM_SUCCESS:
	return("PAM_SUCCESS");
      case PAM_OPEN_ERR:
	return("PAM_OPEN_ERR");
      case PAM_SYMBOL_ERR:
	return("PAM_SYMBOL_ERR");
      case PAM_SERVICE_ERR:
	return("PAM_SERVICE_ERR");
      case PAM_SYSTEM_ERR:
	return("PAM_SYSTEM_ERR");
      case PAM_BUF_ERR:
	return("PAM_BUF_ERR");
      case PAM_CONV_ERR:
	return("PAM_CONV_ERR");
      case PAM_PERM_DENIED:
	return("PAM_PERM_DENIED");
      case PAM_AUTH_ERR:
	return("PAM_AUTH_ERR");
      case PAM_CRED_INSUFFICIENT:
	return("PAM_CRED_INSUFFICIENT");
      case PAM_AUTHINFO_UNAVAIL:
	return("PAM_AUTHINFO_UNAVAIL");
      case PAM_USER_UNKNOWN:
	return("PAM_USER_UNKNOWN");
      case PAM_MAXTRIES:
	return("PAM_MAXTRIES");
      default:
	snprintf(retbuf, sizeof(retbuf), "Unknown PAM error %d\n", errcode);
	return(retbuf);
    }
}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]