/*___INFO__MARK_BEGIN__*/ /************************************************************************* * * The Contents of this file are made available subject to the terms of * the Sun Industry Standards Source License Version 1.2 * * Sun Microsystems Inc., March, 2001 * * * Sun Industry Standards Source License Version 1.2 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.2 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2001 by Sun Microsystems, Inc. * * All Rights Reserved. * ************************************************************************/ /*___INFO__MARK_END__*/ #include #include #include #include #include #ifdef WINDOWS # define _WIN32_WINNT 0x0500 # include # include # include # include /* * for some reason we cannot include basistypes.h when building authuser. * only include it ifndef WINDOWS in msg_juti.h, * and do special handling here. * TODO: should be understood and fixed (in basis_types.h?) */ # define SFN "%-.100s" # define _(x) (x) # define _MESSAGE(x,y) (y) #else # include # if !(defined(DARWIN) || defined(FREEBSD) || defined(NETBSD)) # include # endif # include #include #endif #if defined(IRIX65) || defined(AIX43) || defined(HP1164) || defined(INTERIX) || defined(ALPHA5) || defined(WINDOWS) #define JUTI_NO_PAM #elif defined(DARWIN) #include #else #include #endif #if defined(DARWIN) || defined(AIX) || defined(INTERIX) || defined(FREEBSD) || defined(ALPHA5) || defined(WINDOWS) || defined(NETBSD) #define JUTI_NO_SHADOW #else #include #endif #if defined(AIX) #include #endif #include "msg_utilbin.h" #include "juti.h" #if 0 #define DEBUG 1 #endif #define BUF_SIZE 1024 #ifdef WINDOWS typedef int gid_t; #endif typedef struct error_handler_str error_handler_t; struct error_handler_str { void (*error)(const char* fmt, ...); }; typedef enum { JUTI_AUTH_SUCCESS = 0, /* authentication success */ JUTI_AUTH_FAILED = 1, /* invalid username or password */ JUTI_AUTH_ERROR = 2 /* authentication is not proper configured */ } auth_result_t; static void usage(void); static void print_error(const char* format, ...); static int mygetline(char s[], int lim); static auth_result_t do_system_authentication(const char *username, const char *password, int *uid, int *gid, error_handler_t *error_handler); #ifdef WINDOWS static auth_result_t do_windows_system_authentication(const char *username, const char *password, char ***pppszUID, char ***pppszGID, char ***pppszGroupNames, int *nGIDs, error_handler_t *error_handler); #else static auth_result_t do_pam_authenticate(const char* service, const char *username, const char *password, error_handler_t *error_handler); static auth_result_t get_crypted_password(const char* username, char* buffer, size_t size, error_handler_t *error_handler); static int juti_getgrouplist(const char *uname, gid_t agroup, gid_t **groups_res, char*** group_names, int *grpcnt); static int add_group(struct group *grp, gid_t **groups, char*** group_names, int* ngroups, int* maxgroups); #endif static void usage(void) { fprintf(stderr, "authuser \n"); fprintf(stderr, "\n"); fprintf(stderr, " "); #ifndef JUTI_NO_PAM fprintf(stderr, "\"pam\" or "); #endif fprintf(stderr, "\"system\"\n"); fprintf(stderr, "\n"); #ifndef JUTI_NO_PAM fprintf(stderr, "Params for PAM authentification\n"); fprintf(stderr, " -s name of the pam service\n"); #endif fprintf(stderr, "\n"); fprintf(stderr, " and are read from stdin\n"); exit(1); } static void print_error(const char* format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, MSG_AUTHUSER_ERROR); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); va_end(ap); } int main(int argc, char** argv) { const char *auth_method = NULL; const char *service = NULL; char username[BUF_SIZE]; char password[BUF_SIZE]; int i = 0; int ret = 0; error_handler_t error_handler; int uid = 0; int gid = 0; #ifdef WINDOWS char **ppszUID = NULL; char **ppszGID = NULL; char **ppszGroupNames = NULL; int nGIDs; #endif error_handler.error = print_error; if(argc < 2 ) { print_error(MSG_AUTUSER_INVAILD_ARG_COUNT); usage(); } auth_method = argv[1]; #ifndef WINDOWS #define SGE_SUPERUSER_UID 0 /* only root can successfull execute this */ if(geteuid() != SGE_SUPERUSER_UID && geteuid() != getuid()) { print_error(MSG_AUTHUSER_ONLY_ROOT_S, argv[0]); return 1; } #endif if(strcmp(auth_method, "pam") == 0 ) { for(i=2; i < argc; i++) { if( strcmp(argv[i], "-s") == 0 ) { i++; if(i >= argc) { print_error(MSG_AUTUSER_MISSING_PAM_SERVICE); usage(); } service = argv[i]; } else { print_error(MSG_AUTUSER_UNKNOWN_PARAM_S, argv[i]); usage(); } } if (service == NULL) { print_error(MSG_AUTUSER_MISSING_PAM_SERVICE); usage(); } } else if (strcmp(auth_method, "system" ) == 0 ) { } else { print_error(MSG_AUTUSER_UNKNOWN_AUTH_METHOD_S, auth_method); usage(); } fprintf(stdout, "username: "); fflush(stdout); if(mygetline(username, BUF_SIZE-1) <= 0) { usage(); return 1; } fprintf(stdout, "password: "); fflush(stdout); setEcho(0); if(mygetline(password, BUF_SIZE-1) < 0) { setEcho(1); usage(); return 1; } fprintf(stdout,"\n"); setEcho(1); if (strcmp(auth_method, "pam") == 0 ) { #ifndef WINDOWS ret = do_pam_authenticate(service, username, password, &error_handler); if(ret == JUTI_AUTH_SUCCESS) { struct passwd *pw = getpwnam(username); if(pw == NULL) { print_error(MSG_AUTHUSER_NO_PW_ENTRY_SS, username, strerror(errno)); return -1; } uid = pw->pw_uid; gid = pw->pw_gid; } #else ret = JUTI_AUTH_ERROR; print_error(MSG_AUTHUSER_PAM_NOT_AVAILABLE); #endif } else if(strcmp(auth_method, "system") == 0 ) { #ifndef WINDOWS ret = do_system_authentication(username, password, &uid, &gid, &error_handler); #else ret = do_windows_system_authentication(username, password, &ppszUID, &ppszGID, &ppszGroupNames, &nGIDs, &error_handler); #endif } else { ret = -1; } if (ret==JUTI_AUTH_SUCCESS) { int group_count = 0; gid_t *groups = NULL; char **group_names = NULL; #ifndef WINDOWS fprintf(stdout, "uid %d\n", uid); fprintf(stdout, "gid "); if(juti_getgrouplist(username, gid, &groups, &group_names, &group_count) == 0) { for(i = 0; i < group_count; i++) { if(i>0) { fprintf(stdout, ",%s("gid_t_fmt")", group_names[i], groups[i]); } else { fprintf(stdout, "%s("gid_t_fmt")", group_names[i], groups[i]); } free(group_names[i]); } free(groups); free(group_names); } #else fprintf(stdout, "uid %s\n", *ppszUID); LocalFree(ppszUID[0]); free(ppszUID); fprintf(stdout, "gid "); for(i=0; i0) { fprintf(stdout, ",%s(%s)", ppszGroupNames[i], ppszGID[i]); } else { fprintf(stdout, "%s(%s)", ppszGroupNames[i], ppszGID[i]); } LocalFree(ppszGID[i]); free(ppszGroupNames[i]); } free(ppszGID); free(ppszGroupNames); #endif fprintf(stdout, "\n"); } return ret; } static int mygetline(char s[], int lim) { /* get a line into s, return length */ /* User data is sent one field to a line, with the data set terminated when a line containing only a semi-colon is received. */ int c, i; for (i = 0; i < lim-1 && ((c = getchar()) != EOF) && c != '\n'; i++) { s[i] = c; } s[i] = '\0'; return (i); } #ifndef WINDOWS static auth_result_t do_system_authentication(const char *username, const char *password, int *uid, int *gid, error_handler_t *error_handler) { auth_result_t ret = JUTI_AUTH_SUCCESS; char crypted_password[BUF_SIZE]; char *new_crypted_password = NULL; struct passwd *pwres = getpwnam(username); if(pwres == NULL) { ret = JUTI_AUTH_FAILED; error_handler->error(MSG_AUTHUSER_USER_UNKNOWN_S, username); goto error; } #ifdef DEBUG printf("user found in passwd\n"); #endif ret = get_crypted_password(username, crypted_password, BUF_SIZE, error_handler); if(ret != JUTI_AUTH_SUCCESS) { goto error; } #ifdef DEBUG printf(" crypted password: %s\n", crypted_password); #endif #if !defined(INTERIX) new_crypted_password = crypt(password, crypted_password); #endif if (new_crypted_password == NULL) { error_handler->error(MSG_AUTHUSER_CRYPT_FAILED_S, strerror(errno)); ret = JUTI_AUTH_ERROR; } else { #ifdef DEBUG printf("new crypted password: %s\n", new_crypted_password); #endif if(strcmp(crypted_password, new_crypted_password) == 0) { ret = JUTI_AUTH_SUCCESS; } else { error_handler->error(MSG_AUTHUSER_INVALID_PASSWORD); ret = JUTI_AUTH_FAILED; } } *uid = pwres->pw_uid; *gid = pwres->pw_gid; error: return ret; } #ifndef JUTI_NO_PAM /* * For application data, like password. Passes through the * pam framework to the conversation function. */ struct app_pam_data { const char *password; }; /* pam conversation function */ #if defined(SOLARIS) || defined(AIX) || defined(HP11) || defined(HP1164) static int login_conv(int num_msg, struct pam_message **msgm, struct pam_response **response, void *appdata_ptr); #else static int login_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr); #endif #endif static auth_result_t do_pam_authenticate(const char* service, const char *username, const char *password, error_handler_t *error_handler) { #ifdef JUTI_NO_PAM error_handler->error(MSG_AUTHUSER_PAM_NOT_AVAILABLE); return JUTI_AUTH_ERROR; #else auth_result_t ret; int status; pam_handle_t *pamh; /* pam handle */ struct pam_conv pamconv; /* pam init structure */ struct app_pam_data app_data; /* pam application data */ pamh = NULL; /* * app_data gets passed through the framework * to "login_conv". Set the password for use in login_conv * and set up the pam_conv data with the conversation * function and the application data pointer. */ app_data.password = password; pamconv.conv = login_conv; pamconv.appdata_ptr = &app_data; /* pam start session */ status = pam_start(service, username, &pamconv, &pamh); if(status != PAM_SUCCESS) { ret = JUTI_AUTH_ERROR; goto error; } status = pam_authenticate(pamh, PAM_SILENT); if(status != PAM_SUCCESS) { ret = JUTI_AUTH_FAILED; goto error; } /* check if the authenicated user is allowed to use machine */ status = pam_acct_mgmt(pamh, PAM_SILENT); if (status != PAM_SUCCESS) { ret = JUTI_AUTH_FAILED; goto error; } ret = JUTI_AUTH_SUCCESS; error: if (status != PAM_SUCCESS) { const char *pam_err_msg = pam_strerror(pamh, status); error_handler->error(MSG_AUTHUSER_PAM_ERROR_S, pam_err_msg); } /* end pam session */ if (pamh != NULL) { pam_end(pamh, status == PAM_SUCCESS ? PAM_SUCCESS : PAM_ABORT); } return ret; #endif } /* * login_conv: * this is the conversation function called from a PAM authentication module * to print erro messagae or get user information */ #ifndef JUTI_NO_PAM #if defined(SOLARIS) || defined(AIX) || defined(HP11) || defined(HP1164) static int login_conv(int num_msg, struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) #else static int login_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) #endif { int count = 0; int reply_used = 0; struct pam_response *reply; if (num_msg <= 0) { #ifdef DEBUG printf("no message returned from pam module\n"); #endif return (PAM_CONV_ERR); } reply = (struct pam_response *)calloc(num_msg, sizeof (struct pam_response)); if (reply == NULL) { #ifdef DEBUG printf("calloc memory error\n"); #endif return (PAM_BUF_ERR); } for (count = 0; count < num_msg; ++count) { #ifdef DEBUG printf("\n login_conv: prompt_style: %d" , msgm[count]->msg_style); printf("\n login_conv: prompt_message: %s" , msgm[count]->msg); printf("\n PAM_PROMPT_ECHO_OFF %d" , PAM_PROMPT_ECHO_OFF); printf("\n PAM_PROMPT_ECHO_ON %d" , PAM_PROMPT_ECHO_ON); printf("\n PAM_ERROR_MSG %d" , PAM_ERROR_MSG); printf("\n PAM_TEXT_INFO %d" , PAM_TEXT_INFO); #endif switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_OFF: if (((struct app_pam_data *)appdata_ptr)->password != NULL) { (reply+count)->resp = strdup(((struct app_pam_data *) appdata_ptr)->password); if (reply[count].resp == NULL) { /* * It may be the case that some modules won't free * the pam_response memory if the return is not * PAM_SUCCESS. We should not have had * multiple PAM_PROMPT_ECHO_OFF in a single * login_conv call but just in case see if * reply was modified anyway. */ if (reply_used) { int i; for (i = 0; i < num_msg; ++i) { if (reply[i].resp != NULL) { free(reply[i].resp); } } } free(reply); *response = NULL; return (PAM_BUF_ERR); } } /* * Always set reply_used even if the password is NULL * A NULL response cannot be returned with PAM_SUCCESS. * Could return PAN_CONV_ERR but let later code determine * the effect of a NULL password. */ reply_used = 1; break; /* For empty user name, shouldn't happen */ case PAM_PROMPT_ECHO_ON: /* * It may be the case that some modules won't free * the pam_response memory if the return is not * PAM_SUCCESS. We should not have had * multiple PAM_PROMPT_ECHO_OFF in a single * login_conv call but just in case see if * reply was modified anyway. */ if (reply_used) { int i; for (i = 0; i < num_msg; ++i) { if (reply[i].resp != NULL) { free(reply[i].resp); } } } free(reply); *response = NULL; /* * Have to return error here since PAM can loop * when no username is provided */ break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: break; default: break; } } /* * The response may not always be freed by * modules for PAM_ERROR_MSG, and PAM_TEXT_INFO * So make sure it is freed if we didn't use it. */ if (!reply_used) { free(reply); reply = NULL; } *response = reply; return (PAM_SUCCESS); } #endif static auth_result_t get_crypted_password(const char* username, char* buffer, size_t size, error_handler_t *error_handler) { #if defined(AIX) #define BUFSIZE 1024 char buf[BUFSIZE]; struct userpw *pw = NULL; strncpy(buf, username, BUFSIZE); pw = getuserpw(buf); if(pw == NULL) { error_handler->error(MSG_AUTHUSER_USER_UNKNOWN_S, username); return JUTI_AUTH_FAILED; } else { strncpy(pw->upw_passwd, buffer, size); return JUTI_AUTH_SUCCESS; } #else struct passwd *pw = getpwnam(username); if(pw == NULL ) { error_handler->error(MSG_AUTHUSER_USER_UNKNOWN_S, username); return JUTI_AUTH_FAILED; #if !defined(JUTI_NO_SHADOW) /* On linux the getpwnam returns the password "x" if the user has a shadow entry and authuser is not started as user root */ } else if (strcmp("x", pw->pw_passwd) == 0) { struct spwd *pres = NULL; #ifdef DEBUG printf("getpwnam did not return the crypted passwd, try getspnam\n"); #endif pres = getspnam(username); if(pres == NULL) { error_handler->error(MSG_AUTHUSER_NO_SHADOW_ENTRY_S, username); return JUTI_AUTH_FAILED; } strncpy(buffer, pres->sp_pwdp, size); #endif } else { strncpy(buffer, pw->pw_passwd, size); } #endif return JUTI_AUTH_SUCCESS; } static int add_group(struct group *grp, gid_t **groups_res, char*** group_names_res, int* ngroups_res, int* maxgroups_res) { int ngroups = *ngroups_res; int maxgroups = *maxgroups_res; gid_t *groups = *groups_res; char** group_names = *group_names_res; if (ngroups >= maxgroups) { gid_t *new_groups; char **new_group_names; maxgroups +=10; new_groups = (gid_t*)realloc(groups, sizeof(gid_t) * maxgroups); if (new_groups == NULL) { return -1; } groups = new_groups; new_group_names = (char**)realloc(group_names, sizeof(char*) * maxgroups); if(new_group_names == NULL) { return -1; } group_names = new_group_names; } groups[ngroups] = grp->gr_gid; group_names[ngroups++] = strdup(grp->gr_name); *ngroups_res = ngroups; *maxgroups_res = maxgroups; *groups_res = groups; *group_names_res = group_names; return 0; } static int juti_getgrouplist(const char *uname, gid_t agroup, gid_t **groups_res, char*** group_names_res, int *grpcnt) { struct group *grp; int bail; int ret = 0; int maxgroups = 0; gid_t *groups = NULL; char **group_names = NULL; int ngroups = 0; int i = 0; setgrent(); while ((grp = getgrent())) { for (bail = 0, i = 0; bail == 0 && i < ngroups; i++) { if (groups[i] == grp->gr_gid) { bail = 1; } } if (bail) { continue; } if (grp->gr_gid == agroup) { if(add_group(grp, &groups, &group_names, &ngroups, &maxgroups) != 0) { ret = -1; goto error; } continue; } for (i = 0; grp->gr_mem[i]; i++) { if (strcmp(grp->gr_mem[i], uname) == 0) { if(add_group(grp, &groups, &group_names, &ngroups, &maxgroups) != 0) { ret = -1; goto error; } } } } endgrent(); error: if (ret != 0) { if (groups != NULL) { free(groups); } for (i=0; i < ngroups; i++) { free(group_names[i]); } if (group_names != NULL) { free(group_names); } } else { *groups_res = groups; *group_names_res = group_names; *grpcnt = ngroups; } return ret; } #else /* WINDOWS is defined */ static int GetSidStrings(HANDLE hToken, TOKEN_INFORMATION_CLASS TokenInfo, char ***pppszSid, char ***pppszGroupNames, int *nStrings) { #define MAX_DOMAIN_NAME 1000 #define MAX_GROUP_NAME 1000 LPVOID pTokenInfo = NULL; DWORD dwLength; DWORD i; int ret = 0; SID_NAME_USE SidNameUse; char szDomainName[MAX_DOMAIN_NAME]; char szGroupName[MAX_GROUP_NAME]; DWORD dwMaxDomainName = MAX_DOMAIN_NAME; DWORD dwMaxGroupName = MAX_GROUP_NAME; GetTokenInformation(hToken, TokenInfo, (LPVOID)pTokenInfo, 0, &dwLength); if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { ret = 1; goto error; } pTokenInfo = (PTOKEN_OWNER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); if(pTokenInfo == NULL) { ret = 2; goto error; } if(!GetTokenInformation(hToken, TokenInfo, (LPVOID)pTokenInfo, dwLength, &dwLength)) { ret = 3; goto error; } if(TokenInfo == TokenOwner) { TOKEN_OWNER *pTokenOwner = (TOKEN_OWNER*)pTokenInfo; *pppszSid = (char**)malloc(sizeof(char*)); ConvertSidToStringSid(pTokenOwner->Owner, *pppszSid); } else if(TokenInfo == TokenGroups) { TOKEN_GROUPS *pTokenGroups = (TOKEN_GROUPS*)pTokenInfo; *pppszSid = (char**)malloc(sizeof(char*) * pTokenGroups->GroupCount); *pppszGroupNames = (char**)malloc(sizeof(char*) * pTokenGroups->GroupCount); *nStrings = pTokenGroups->GroupCount; for(i=0; iGroupCount; i++) { ConvertSidToStringSid(pTokenGroups->Groups[i].Sid, &((*pppszSid)[i])); LookupAccountSid(NULL, pTokenGroups->Groups[i].Sid, szGroupName, &dwMaxGroupName, szDomainName, &dwMaxDomainName, &SidNameUse); (*pppszGroupNames)[i] = (char*)malloc(sizeof(char) * (strlen(szDomainName) + strlen(szGroupName) + 2)); sprintf((*pppszGroupNames)[i], "%s\\%s", szDomainName, szGroupName); } } error: HeapFree(GetProcessHeap(), 0, pTokenInfo); return ret; } static auth_result_t do_windows_system_authentication(const char *username, const char *password, char ***pppszUID, char ***pppszGID, char ***pppszGroupNames, int *nGIDs, error_handler_t *error_handler) { auth_result_t ret = JUTI_AUTH_SUCCESS; HANDLE hToken = INVALID_HANDLE_VALUE; int nUIDs = 0; char *domain; char *userbuf; char *user; char *backslash; char *buf = NULL; DWORD buf_size = 0; DOMAIN_CONTROLLER_INFO *pbuf; DWORD dwRes; /* * username can be in format "domain\username", split it up. */ userbuf = strdup(username); user=userbuf; backslash = strchr(user, '\\'); if(backslash != NULL) { domain = user; user = backslash+1; *backslash = '\0'; } else { /* * if no domain was provided, use primary domain by default */ dwRes = DsGetDcName(NULL, NULL, NULL, NULL, DS_RETURN_FLAT_NAME|DS_PDC_REQUIRED, &pbuf); if(dwRes == ERROR_SUCCESS && pbuf->DomainName != NULL) { domain = pbuf->DomainName; } else { /* * no primary domain, use local host as domain */ domain = "."; } } if(!LogonUser( user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken)) { ret = JUTI_AUTH_ERROR; error_handler->error(MSG_AUTHUSER_WRONG_USER_OR_PASSWORD); goto error; } GetSidStrings(hToken, TokenOwner, pppszUID, NULL, &nUIDs); GetSidStrings(hToken, TokenGroups, pppszGID, pppszGroupNames, nGIDs); CloseHandle(hToken); error: free(userbuf); if(pbuf != NULL) { NetApiBufferFree(pbuf); } return ret; } #endif