FriendUP HQ

Please visit the main source of project information on the FriendUP website:

Authentication using PAM

This topic contains 10 replies, has 2 voices, and was last updated by  stefkos 1 year, 3 months ago.

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #777

    Grim
    Participant

    I would like to integrate with my current infrastructure so started by writing an authmod for PAM. This works fine except the users need to be added to FriendUP before they can login. Whould it go against the design ideas to let the authmod add users? Otherwise, how do I do it. If the Authenticate() method is required to call the (possibly overloaded?) CheckPassword)= a User object must exist before PAM can check the password. Or is it okey to baypass CheckPassword() method? Would be prefered to create a temporary User object (how?) and dispose of it if CheckPassword() fails?

    Here is the relevant code

    static int pam_callback(
        int count,
        const struct pam_message** msg,
        struct pam_response** resp,
        void* userdata
    ){
        char* pwd = (char*)userdata;
        int i;
        for(i=0;i<count;++i){
            resp[i] = FMalloc(sizeof(struct pam_response));
            resp[i]->resp = StringDuplicate(pwd);
            resp[i]->resp_retcode = 0;
        }
        return PAM_SUCCESS;
    }
    
    FBOOL CheckPassword(
        struct AuthMod* am,
        Http* r,
        User* usr,
        char* pwd,
        FULONG* blocktime
    ){
        struct pam_conv pamc;
        pam_handle_t* pamh;
        int retval;
        if(!usr)
            return FALSE;
        pamc.conv = pam_callback;
        pamc.appdata_ptr = (void*)pwd;
        /* retval = */ pam_start("friendup",usr->u_Name,&pamc,&pamh);
        retval = pam_authenticate(pamh,0);
        pam_end(pamh,retval);
        return PAM_SUCCESS == retval;
    }
    
    UserSession* Authenticate(
        struct AuthMod* am,
        Http* r,
        struct UserSession* sess,
        char* name,
        char* pwd,
        char* devname,
        char* sessid,
        FULONG* blocktime
    ){
        SystemBase* sb = am->sb;
        time_t timestamp;
        DEBUG("[PAMAUTH] Authenticate START (%s)\n",name);
        timestamp = time(NULL);
        if(sessid){
            sess = sb->sl_UserSessionManagerInterface.USMGetSessionBySessionID(sb->sl_USM,sessid);
            if(!sess)
                goto fail;
            if(strcmp(name,sess->us_User->u_Name))
                goto fail;
        }else{
            User* usr;
            if(sess && sess->us_User){
                usr = sess->us_User;
            }else{
                usr = sb->sl_UserManagerInterface.UMUserGetByNameDB(sb->sl_UM,name);
                if(!usr)
                    goto fail;
                if(!sess)
                    sess = USMGetSessionByDeviceIDandUser(sb->sl_USM,devname,usr->u_ID);
            }
            if(!am->CheckPassword(am,r,usr,pwd,blocktime))
                goto fail;
            if(!sess){
                sess = UserSessionNew(NULL,devname);
                sess->us_UserID = usr->u_ID;
            }
            u_strset_steal(&sess->us_SessionID,create_session_id(timestamp,usr));
        }
        sess->us_LoggedTime = timestamp;
        sb->sl_UserManagerInterface.UMStoreLoginAttempt(sb->sl_UM,name,"Login success",NULL);
        return sess;
    fail:
        sb->sl_UserManagerInterface.UMStoreLoginAttempt(sb->sl_UM,name,"Login fail","Login fail");
        return NULL;
    }
    

    For this to work, client side password hashing must be disabled. Here is a patch for that:

    diff --git a/interfaces/web_desktop/js/workspace.js b/interfaces/web_desktop/js/workspace.js
    index 5171b063..e45004cc 100755
    --- a/interfaces/web_desktop/js/workspace.js
    +++ b/interfaces/web_desktop/js/workspace.js
    @@ -1258,15 +1258,7 @@ Workspace = {
     
                    var t = this;
                    this.loginUsername = u;
    -               
    -               if( p.indexOf('HASHED') == 0 )
    -               {
                    this.loginPassword = p;
    -               }
    -               else
    -               {
    -                       this.loginPassword = 'HASHED' + Sha256.hash( p );
    -               }
     
                    if( this.loginUsername && this.loginPassword )
                    {
    

    It also seemed like FriendUP was unwilling to load the configured authmod (instead always faling back to the default). Here is a patch for that:

    diff --git a/core/system/systembase.c b/core/system/systembase.c
    index d5509c28..fea612b2 100644
    --- a/core/system/systembase.c
    +++ b/core/system/systembase.c
    @@ -522,62 +522,18 @@ SystemBase *SystemInit( void )
                    closedir( d );
            }
     
    -       char defaultAuth[ 128 ];
    -       defaultAuth[ 0 ] = 0;
    -       char *def = NULL;
    -       strcpy( defaultAuth, "fcdb.authmod" );
    -       
    -/*
    -       {
    -               struct PropertiesLibrary *plib = NULL;
    -               Props *prop = NULL;
    -       
    -               if( ( plib = (struct PropertiesLibrary *)LibraryOpen( l, "properties.library", 0 ) ) != NULL )
    -               {
    -                       char *ptr, path[ 1024 ];
    -                       path[ 0 ] = 0;
    -       
    -                       ptr = getenv("FRIEND_HOME");
    -                       if( ptr != NULL )
    -                       {
    -                               sprintf( path, "%scfg/cfg.ini", ptr );
    -                       }
    -
    -                       prop = plib->Open( path );
    -                       if( prop != NULL)
    -                       {
    -
    -                               char *tmp  = plib->ReadString( prop, "LoginModules:use", "fcdb.authmod" );
    -                               if( tmp != NULL )
    +       if( l->sl_ActiveModuleName == NULL )
            {
    -                                       strcpy( defaultAuth, tmp );
    -                               }
    -                               
    -                               plib->Close( prop );
    -                       }
    -                       
    -                       LibraryClose( ( struct Library *)plib );
    -               }
    -       }
    -*/
    -       if( l->sl_ActiveModuleName != NULL )
    -       {
    -               def = l->sl_ActiveModuleName;
    -       }
    -       else
    -       {
    -               def = defaultAuth;
    -               l->sl_ActiveModuleName = StringDuplicate( def );
    +               l->sl_ActiveModuleName = StringDuplicate( "fcdb.authmod" );
            }
            // Get auth module
    -       if( def != NULL )
            {
                    AuthMod *mod = l->sl_AuthModules;
     
                    while( mod != NULL )
                    {
                            // atm we only need authname to get proper login page
    -                       if( strcmp( mod->am_Name, defaultAuth ) == 0 ) //l->sl_ActiveModuleName ) == 0 )
    +                       if( strcmp( mod->am_Name, l->sl_ActiveModuleName ) == 0 )
                            {
                                    l->sl_ActiveAuthModule = mod;
                                    INFO("[SystemBase] Default login module set to : %s\n", l->sl_ActiveAuthModule->am_Name );
    @@ -594,11 +550,6 @@ SystemBase *SystemInit( void )
                            return NULL;
                    }
            }
    -       else
    -       {
    -               FERROR("Default path not provided\n");
    -               return NULL;    
    -       }
     
            Log( FLOG_INFO, "AUTHOD master set to %s\n", l->sl_ActiveAuthModule->am_Name );
     
    
    #796

    stefkos
    Participant

    Hey

    It’s great to see that people like you want to help us improve our product.
    In the current version all authentication is made on site which is returned from FC after call:
    /loginprompt (protocol_http.c line 558)
    You will see that php/login.php is called there and inside there is called functionoality which create user in Friend database.
    But in final version we want to give developer possibility to handle all security calls (Authenticate, CheckPassword, etc.). I think I know how to make this stuff working with your patch but would be nice to discuss here what developers expect.

    #797

    Grim
    Participant

    I see that the code you talk about specifically checks if the authmod in use is fcdb.authmod, otherwise it simply serves the default login page. Since I use a custom authmod, I don’t think PHP is involved(?).

    I would like the authentication to be something like this:

    if session is valid
      return success
    
    if CheckPassword(username,password) fails
      return fail
    
    if user not found
      create user
    
    create session
    
    return success
    

    Currently all this happens in the authmod (in the Authenticate() method). But it’s pretty generic so perhaps FriendUP could provide a default implementation, so the authmod just need to provide CheckPassword()?

    This leads to something I don’t quite understand, which is FriendUP style libraries (I guess this is a heritage from the Amiga). For example how the AuthMod object maps to authmod library files. Each of the (virtual) methods can be individually overridden at runtime (instead of just having a pointer to a static table of functions, C++ style). This gives a great deal of flexibility. What are the policies for overriding methods? In authmodule.c the virtual functions are initialized from a authmod library file by looking up each function using dlsym(). To me, a more obvious approach would be to just lookup one function in the library with dlsym() and let that function fill out the AuthMod struct (only overriding some methods while leaving others with a default implementation). But I guess this goes against some design philosophy, related to the FriendUP view of dynamic libraries?

    • This reply was modified 1 year, 3 months ago by  Grim.
    #799

    stefkos
    Participant

    1st:

    if( activemodule != fcdb ) then:
    
    // Make the commandline string with the safe, escaped arguments, and check for buffer overflows.
    							int cx = snprintf( command, sizeof(command), "php \"%s\" \"%s\" \"%s\" \"%s\";", "php/login.php", uri->path->raw, uri->queryRaw, request->content ); // SLIB->sl_ModuleNames

    and call php with this parameters.
    Inside code you will see that specially prepared site is returned.
    Otherwise just static file which is using fcdb.authmod.

    2nd:
    I must think about that. But indeed, we need function which will create user in any db to which Friend is connected.

    3:
    In cfg.ini there are 2 settings releated to this.
    LoginModules:modules – this point to variable where admin set which data will go to generated login page (php) where currently whole authentication is done (not when fcdb.authmod is selected). And since php have access to db it can create account in FriendDB.
    LoginModules:use – this was designed to take care about whole security calls and pass it to another language (like php). But as you see now, it is used only to display static login page or generated one.

    Your idea is good and I was thinking about that, but there was no time to work on that:(
    I will talk with guys next week, maybe they will assign this task to my and I will clean it a bit.
    My plan:
    1 – make fcdb.authmod default authmod
    2 – another developer will have possibility to overwrite only some functions
    3 – I will create function which will generate default user
    Did I missed something?

    #800

    Grim
    Participant

    I’m not too concerned with what the login page looks like or how the PHP stuff handling that works. But then a user tries to login a request is sent for system.library (protocol_http.c line 476) with the Webreq func login (systembase_web.c line 619). Which calls the authmod to do the actual authentication. From what I can see no PHP is involved in this. Are you saying this is going to be reimplemented in PHP? “LoginModules:modules” sets sl_ModuleNames. Right now that doesn’t seem to be used. But it’s referenced in the php authmod (which is not and will not be the default?).

    I use “LoginModules:use” (together with above patches) to load my custom module. This works fine for me right now (except that I need to add the users to Friend manually). I would prefer if I could write some kind of plugin that grabs user data from my LDAP database. But it doesn’t really contain anything important, so if Friend could automatically create users as needed (then authentication succeeds) that would be okay too. And in that case it wouldn’t matter to me how they were stored. The curent MySQL bases solution would be as good as any. But in the future all kind of local storage will be phased our in favour decentralised storage, I understand from reading the whitepaper.

    I wouldn’t mind working on authentication and related topics in the “kernel”. But I need to understand the bigger picture in order to do it right and not just come up with some patchy hacks. I would appriciate some kind of design document (of the kernel primarily). Perhaps that will be included in a revised version of the Developer’s Manual?

    #803

    stefkos
    Participant

    Hey

    Maybe I wrote that wrong way. I will write to you here how SAML login procedure looks like:
    1 When login page is loaded call /loginprompt is called (protocol_http.c)
    2 If LoginModules is equal to fcdb.authmod then FC return static file, which contain default login form.
    3 If LoginModules is not equal do fcdb.authmod then call is going to php which return other kind of login page.
    Now we will track this option:
    a) user enter his credentials (maybe other required data)
    b) press login.
    c) All calls are going to another pages/sites which check user data.
    d) If everything is ok then user is created in DB (if there is need to do that) and user is passing login page.
    And about C code. Currently it doesnt matter if user will select fcdb.authmod or another. FC is using fcdb.authmod and as you know we must change that and I have idea how (after this discussion), just need a bit time to do that.
    Tomorrow we will have meeting, I will point them this discussion and come here with more info.

    #805

    stefkos
    Participant

    Small update regarding user creation.
    You can create User structure and store it in DB by using
    systembase->sl_UserManagerInterface.>UMUserCreate( systembase->sl_UM, NULL, poitnerToYourUserStructure );
    and then:
    systembase->sl_UserManagerInterface->UMAssignGroupToUserByStringDB( systembase->sl_UM, poitnerToYourUserStructure, “admin” );
    You can put anything in your password field, because later you are checking it in your authmodule.

    #807

    stefkos
    Participant

    OK I think task is done. Just need permission from guys to merge my sources with Open Source version.
    Your plugin should work without changes.

    #826

    stefkos
    Participant

    I commited today fix to OS repository. Please check it and give me feedback.

    #829

    Grim
    Participant

    Yes, it works! But it obviously still need client side hashing to be disabled. As I wrote in the bug forum, I consider client side password hashing to be a flawed idea to begin with. So if the password hashing is to be done server side, it has to be implemented in the fcdb authmod I guess.

    In a more generic sense, how is the authmod system supposed to work? If I understand it correctly, fcdb is a hardcoded default. And exactly one additional authmod can be specified via “LoginModules:use” to overload some virtual methods. Correct? Then why are every authmod loaded? And can’t the default virtual methods equally well be hardcoded themselves (moved from fcdb.c to say, authmodule.c) as they are kind of hardcoded anyway? I just get the feeling stuff can be simplified.

    #830

    stefkos
    Participant

    Oh, I forgot about client side. I will talk with the guys how we can fix that.
    1 fcdb.authmod is hardcoded
    2 ok I can remove this big list and just leave one selected by developer/admin/user
    3 good idea, will go this way.
    Thx for help.

Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.

Comments are closed.