NSS_STATUS LsaNssCommonPasswdGetpwuid( PLSA_NSS_CACHED_HANDLE pConnection, uid_t uid, struct passwd * pResultUser, char * pszBuf, size_t bufLen, int * pErrorNumber ) { int ret = NSS_STATUS_SUCCESS; HANDLE hLsaConnection = NULL; PVOID pUserInfo = NULL; DWORD dwUserInfoLevel = 0; ret = MAP_LSA_ERROR(NULL, LsaNssCommonEnsureConnected(pConnection)); BAIL_ON_NSS_ERROR(ret); hLsaConnection = pConnection->hLsaConnection; ret = MAP_LSA_ERROR(pErrorNumber, LsaFindUserById( hLsaConnection, uid, dwUserInfoLevel, &pUserInfo)); BAIL_ON_NSS_ERROR(ret); ret = MAP_LSA_ERROR(pErrorNumber, LsaNssWriteUserInfo( dwUserInfoLevel, pUserInfo, pResultUser, &pszBuf, bufLen)); BAIL_ON_NSS_ERROR(ret); cleanup: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, pUserInfo); } return ret; error: if (ret != NSS_STATUS_TRYAGAIN && ret != NSS_STATUS_NOTFOUND) { LsaNssCommonCloseConnection(pConnection); } goto cleanup; }
int main(int argc, const char **argv) { poptContext poptContext; int poptResult; uid_t uid; int kq; HANDLE lsaConnection = (HANDLE) NULL; PVOID pUserInfo = NULL; struct kevent event = { 0 }; int numChanges = 1; krb5_context krb5Context = NULL; char krb5FileCachePath[PATH_MAX]; krb5_ccache krb5FileCache = NULL; krb5_ccache krb5MemoryCache = NULL; krb5_cc_cursor krb5Cursor = NULL; krb5_creds krb5Credentials = { 0 }; krb5_principal krb5Principal = NULL; krb5_error_code krb5Error; int exitStatus = 0; DWORD dwError = LW_ERROR_SUCCESS; poptContext = poptGetContext(NULL, argc, argv, Options, 0); while ((poptResult = poptGetNextOpt(poptContext)) >= 0) { /* All options are processed automatically. */ } if (poptResult < -1) { fprintf(stderr, "%s: %s: %s\n", getprogname(), poptBadOption(poptContext, POPT_BADOPTION_NOALIAS), poptStrerror(poptResult)); exitStatus = 1; goto error; } uid = getuid(); /* Make sure we're running as an AD user. */ dwError = LsaOpenServer(&lsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaFindUserById( lsaConnection, uid, 0, &pUserInfo); if (dwError == LW_ERROR_NO_SUCH_USER) { /* * Running as a non-AD user; exit 0 so launchd doesn't restart * the ticketcopy program (see com.beyondtrust.pbis.ticketcopy.plist). */ LSA_LOG_DEBUG( "uid %lu is not an AD user; exiting", (unsigned long) uid); dwError = LW_ERROR_SUCCESS; goto cleanup; } BAIL_ON_LSA_ERROR(dwError); kq = kqueue(); BAIL_ON_UNIX_ERROR(kq == -1); krb5Error = krb5_init_context(&krb5Context); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5Error = krb5_cc_default(krb5Context, &krb5MemoryCache); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); snprintf( krb5FileCachePath, sizeof(krb5FileCachePath), "FILE:/tmp/krb5cc_%lu", (unsigned long) uid); while (1) /* Forever (or until an error occurs) */ { while ((event.ident = open(krb5FileCachePath + 5, O_RDONLY)) == -1) { sleep(5); } event.filter = EVFILT_VNODE; event.flags = EV_ADD | EV_ENABLE | EV_CLEAR; event.fflags = NOTE_DELETE | NOTE_WRITE; numChanges = 1; krb5Error = krb5_cc_resolve( krb5Context, krb5FileCachePath, &krb5FileCache); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); while (1) /* While the file continues to exist. */ { /* * Turn off KRB5_TC_OPENCLOSE so the file will be opened once * and kept open. This causes it to actually attempt to open * the file, so this is where we check for the file not * existing and retry after sleeping a bit. */ krb5Error = krb5_cc_set_flags(krb5Context, krb5FileCache, 0); if (krb5Error == KRB5_FCC_NOFILE) { break; } BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* Copy all credentials from the file to the memory cache. */ krb5Error = krb5_cc_start_seq_get( krb5Context, krb5FileCache, &krb5Cursor); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); while ((krb5Error = krb5_cc_next_cred( krb5Context, krb5FileCache, &krb5Cursor, &krb5Credentials)) == 0) { krb5Error = krb5_cc_store_cred( krb5Context, krb5MemoryCache, &krb5Credentials); if (krb5Error == KRB5_FCC_NOFILE) { krb5Error = krb5_cc_get_principal( krb5Context, krb5FileCache, &krb5Principal); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* The memory cache was destroyed; re-create it. */ krb5Error = krb5_cc_initialize( krb5Context, krb5MemoryCache, krb5Principal); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5_free_principal(krb5Context, krb5Principal); krb5Principal = NULL; krb5Error = krb5_cc_store_cred( krb5Context, krb5MemoryCache, &krb5Credentials); } BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5_free_cred_contents(krb5Context, &krb5Credentials); } if (krb5Error != KRB5_CC_END) { BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); } krb5Error = krb5_cc_end_seq_get( krb5Context, krb5FileCache, &krb5Cursor); krb5Cursor = NULL; BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* * Turn KRB5_TC_OPENCLOSE back on; this will cause * the file to be closed and any locks to be * released. */ krb5Error = krb5_cc_set_flags( krb5Context, krb5FileCache, KRB5_TC_OPENCLOSE); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* * Wait for the file to be modified or deleted. The first * time this is called after the file is opened, numChanges * will be 1, which will install the fd into the event * list. After that numChanges is changed to 0, so it will * just report events from the existing list. */ if (kevent(kq, &event, numChanges, &event, 1, NULL) != 1) { fprintf(stderr, "kevent failed\n"); exitStatus = 1; goto cleanup; } if (event.fflags & NOTE_DELETE) { break; } numChanges = 0; } krb5Error = krb5_cc_close(krb5Context, krb5FileCache); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5FileCache = NULL; close(event.ident); event.ident = -1; /* * The cache file is usually removed as part of a * rename(2) system call, so only wait a short * time before the first attempt to re-open it. */ usleep(100000); } error: cleanup: krb5_free_cred_contents(krb5Context, &krb5Credentials); if (krb5Cursor) { krb5_cc_end_seq_get(krb5Context, krb5FileCache, &krb5Cursor); } if (krb5FileCache) { krb5_cc_close(krb5Context, krb5FileCache); } if (krb5Principal) { krb5_free_principal(krb5Context, krb5Principal); } if (krb5Context) { krb5_free_context(krb5Context); } if (event.ident != -1) { close(event.ident); } if (pUserInfo) { LsaFreeUserInfo(0, pUserInfo); } if (lsaConnection != (HANDLE) NULL) { LsaCloseServer(lsaConnection); } if (dwError) { exitStatus = 1; } return exitStatus; }