/* SMArT Check username/password combination using PAM and require membership in the configured SMArT administrator Unix group. Copyright 2001 Wilmer van der Gaast Updated for MARS_NWE SMArT group-restricted login. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr); static int user_in_group(const char *username, const char *groupname); static struct pam_conv conv = { my_conv, NULL }; char *user; char *pass; int main( int argc, char **argv ) { pam_handle_t *pamh = NULL; int retval, st = 1; const char *admin_group; if( argc < 4 ) { fprintf( stderr, "Usage: %s \n", argv[0] ); return( 3 ); } user = argv[1]; pass = argv[2]; admin_group = argv[3]; if( user == NULL || user[0] == '\0' || pass == NULL || admin_group == NULL || admin_group[0] == '\0' ) { return( 3 ); } retval = pam_start( "smart", user, &conv, &pamh ); if( retval == PAM_SUCCESS ) retval = pam_authenticate( pamh, PAM_SILENT ); if( retval == PAM_SUCCESS ) st = retval = pam_acct_mgmt( pamh, PAM_SILENT ); if( pamh != NULL && pam_end( pamh, retval ) != PAM_SUCCESS ) return( 1 ); if( st != PAM_SUCCESS ) return( 1 ); if( ! user_in_group( user, admin_group ) ) return( 2 ); return( 0 ); } static int user_in_group(const char *username, const char *groupname) { struct passwd *pw; struct group *gr; int ngroups = 0; gid_t *groups; int i; if( username == NULL || username[0] == '\0' || groupname == NULL || groupname[0] == '\0' ) { return( 0 ); } pw = getpwnam( username ); gr = getgrnam( groupname ); if( pw == NULL || gr == NULL ) { return( 0 ); } if( pw->pw_gid == gr->gr_gid ) { return( 1 ); } #if defined(__linux__) || defined(__GLIBC__) /* getgrouplist() asks NSS for supplementary groups, so files, LDAP, SSSD, NIS, etc. follow the local nsswitch.conf configuration. */ getgrouplist( username, pw->pw_gid, NULL, &ngroups ); if( ngroups > 0 ) { groups = calloc( (size_t) ngroups, sizeof( gid_t ) ); if( groups != NULL ) { if( getgrouplist( username, pw->pw_gid, groups, &ngroups ) >= 0 ) { for( i = 0; i < ngroups; i++ ) { if( groups[i] == gr->gr_gid ) { free( groups ); return( 1 ); } } } free( groups ); } } #endif /* Portable fallback: check the group's explicit member list. */ if( gr->gr_mem != NULL ) { for( i = 0; gr->gr_mem[i] != NULL; i++ ) { if( strcmp( gr->gr_mem[i], username ) == 0 ) { return( 1 ); } } } return( 0 ); } int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { struct pam_response *reply; int i; (void) msg; (void) appdata_ptr; reply = (struct pam_response *) calloc( (size_t) num_msg, sizeof( struct pam_response ) ); if( reply == NULL ) { return( PAM_BUF_ERR ); } for( i = 0; i < num_msg; i ++ ) { reply[i].resp = (char *) strdup( pass ); /* Just give the password... It's all we know */ reply[i].resp_retcode = 0; } *resp = reply; return( PAM_SUCCESS ); }