I have written some C code to connect to a Kerberized LDAP server. This all works fine, but at present, it currently generates a new TGT every time it connects, rather than using the one (assuming it already exists) in the default credentials cache.
I have looked into using the likes of krb5_cc_resolve and krb5_initialize to get a reference to the cache, but this seems to destroy the cache if it already exists, along with any tickets it holds.
Basically, what I want to know is: is there any way of checking the default credentials cache for existing TGTs without destroying it?
krb5_cc_initialize clears the cache, as the documentation says. Just don't do that if you want to access an existing cache
From the docs:
Any existing credentials are discarded and the principal name for the cache is set to the value specified
Look in the code for kstart where it implements the -H option.
http://git.eyrie.org/?p=kerberos/kstart.git;a=blob;f=framework.c;h=66e851413a9b4d71fa4d61ded2f3c0d71cd03b0c;hb=HEAD
Basically, you need to check the expire time for the principal in the ticket.
/* Obtain the ticket. */
memset(&increds, 0, sizeof(increds));
code = krb5_cc_resolve(ctx, config->cache, &ccache);
if (code != 0)
goto done;
increds.client = config->client;
else {
code = krb5_cc_get_principal(ctx, ccache, &increds.client);
if (code != 0)
goto done;
}
code = get_krbtgt_princ(ctx, increds.client, &increds.server);
if (code != 0)
goto done;
code = krb5_get_credentials(ctx, 0, ccache, &increds, &outcreds);
if (code != 0)
goto done;
increds_valid = true;
/* Check the expiration time and renewal limit. */
if (code == 0) {
now = time(NULL);
then = outcreds->times.endtime;
if (config->happy_ticket > 0)
offset = 60 * config->happy_ticket;
else
offset = 60 * config->keep_ticket + EXPIRE_FUDGE;
if (then < now + offset)
code = KRB5KRB_AP_ERR_TKT_EXPIRED;
Related
I've got an SSIS package that is responsible for picking up and loading files to SQL tables, and then routing the files to various archive locations depending upon the content of the file. Prior to doing any loading, however, I have some scripts that check for various file-level errors, one of which is if the file is empty or not. I am using the following script to check if a file is empty:
Dts.Variables["blnEmptyFile"].Value = false;
Dts.Variables["User::strFileTimeStamp"].Value = "_" + DateTime.Now.ToString("yyyyMMdd") + "_" + DateTime.Now.ToString("hhmmss");
if (Dts.Variables["User::strFileRecordType"].Value.ToString() == "AF")
{
Dts.Variables["User::strFileOutToGeBBs"].Value = "AF_" + Dts.Variables["User::strCurrentFileIn"].Value.ToString().Substring(0, Dts.Variables["User::strCurrentFileIn"].Value.ToString().Length - 7) + Dts.Variables["User::strFileTimeStamp"].Value.ToString() + ".txt";
}
else if (Dts.Variables["User::strFileRecordType"].Value.ToString() == "AS")
{
Dts.Variables["User::strFileOutToGeBBs"].Value = "AS_" + Dts.Variables["User::strCurrentFileIn"].Value.ToString().Substring(0, Dts.Variables["User::strCurrentFileIn"].Value.ToString().Length - 7) + Dts.Variables["User::strFileTimeStamp"].Value.ToString() + ".txt";
}
else if (Dts.Variables["User::strFileRecordType"].Value.ToString() == "R")
{
Dts.Variables["User::strFileOutToGeBBs"].Value = "R_" + Dts.Variables["User::strCurrentFileIn"].Value.ToString().Substring(0, Dts.Variables["User::strCurrentFileIn"].Value.ToString().Length - 6) + Dts.Variables["User::strFileTimeStamp"].Value.ToString() + ".txt";
}
else
{
Dts.Variables["User::strFileOutToGeBBs"].Value = Dts.Variables["User::strCurrentFileIn"].Value.ToString().Substring(0, Dts.Variables["User::strCurrentFileIn"].Value.ToString().Length - 4) + Dts.Variables["User::strFileTimeStamp"].Value.ToString() + ".txt";
}
int nonDataRows = 1;
string ffConnection;
ffConnection = (string)(Dts.Connections["CDF_Format"].AcquireConnection(null) as string);
FileInfo flatFileInfo = new FileInfo(ffConnection);
long fileSize = flatFileInfo.Length;
if (fileSize > 0)
{
int lineCount = 0;
StreamReader fsFlatFile = new StreamReader(ffConnection);
while (!(fsFlatFile.EndOfStream))
{
Console.WriteLine(fsFlatFile.ReadLine());
lineCount += 1;
if (lineCount > nonDataRows)
{
Dts.Variables["User::blnEmptyFile"].Value = false;
break;
}
else
{
Dts.Variables["User::blnEmptyFile"].Value = true;
}
}
fsFlatFile.Close();
fsFlatFile = null;
}
else
{
Dts.Variables["User::blnEmptyFile"].Value = true;
}
Dts.TaskResult = (int)ScriptResults.Success;
After this script task, I check the blnEmptyFile variable, and if it is True, I send the empty file to a failed files folder location so that our end users can deal with it. And when I run this script inside the editor (VS 2017/2019), it runs to successful completion every single time. However, when this is run on the ETL servers, it intermittently fails with the following error:
2022-01-18 07:15:14.27 fst_ArchFailedFile:Error: An error occurred with the following error message: "The process cannot access the file because it is being used by another process.".
This only ever happens when a file is empty, and even then, it is only happening with a specific set of files that are empty. And it doesn't happen all the time; it is intermittent, and seems to want to happen on Monday (or, in the case of long weekends, Tuesday) mornings.
As you can see in the code, I am closing fsFlatFile, and I'm setting it to null. ffConnection is a string, so I can't close/kill it...although setting it to NULL has no effect either. I've tried setting a PAUSE, and that also has no effect/bearing on what is happening.
Anybody have any ideas why this would happen, and how I can resolve this? We aren't talking about Excel here (I've seen all of those posts on the internet), but simple TXT files that have only but a header and nothing else in them. Any help here would be keen.
StreamReader still has it. Try using a using block, everything inside is properly closed, disposed, etc:
using(StreamReader fsFlatFile = new StreamReader(ffConnection))
{
All the code you want to do while your stream is open.
}
Can you guys please help me with writing a gamma script for logging data into a database table? The tags are inside a domain created in OPC DA of Cogent Datahub. The only condition that needs to be satisfied is the script should be logging all points in the domain every one second along with their value and timestamp.
require ("Application");
require ("ODBCThreadSupport");
require ("Time");
require ("Quality");
class LogData Application
{
DSN = "your ODBC DSN name"; // The DSN name to use for the database
connection
username = "Database_User"; // The user name for connecting to the
database
password = "*****"; // The password for connecting to the database
tablename = "table1"; // The name of the database table
cachefile = "C:\Users\AppData\cache.txt"; // Base name for the disk cache file
domain = "Domain name"; // The domain in which to log all points
tableclass;
thread;
mappedPoints = new Dictionary();
prevcount = 0;
}
/* If there is something we only want to perform on the first connection, we can
test
* is_first_connect to perform the code only once.
*/
method LogData.onConnect()
{
princ ("Connection succeeded\n");
if (.thread.is_first_connect)
{
// Start the sequence defined by the AddInitStage calls in the constructor
.thread.BeginAsyncInit();
}
}
/* If the connection fails after having been
* connected, this method is called.
*/
method LogData.onConnectFail()
{
princ ("Connection closed: ", SQLResult.Description, "\n");
}
/* Map the table in the set of table definitions that matches the name in
.tablename
* into a Gamma class. This lets us easily convert between class instances and
rows
* in the table.
*/
method LogData.mapTable(name, tabledefinitions)
{
//princ("Mapping table\n");
.tableclass = .thread.ClassFromTable(name, tabledefinitions);
//princ("Table class = ", .tableclass, "\n");
}
method LogData.startLogging()
{
.registerPoints();
}
/* Set up the timer or event handler functions to write to the table. */
method LogData.registerPoints()
{
/* Find all points in the domain */
local info = datahub_domaininfo(.domain)[0];
if (info.n_points != .prevcount)
{
local points = datahub_points(.domain, nil, nil);
local pointsym;
princ(info.n_points - .prevcount, " new points are being added to
logging\n");
with point in points do
{
// Filter out branch points
if ((point.flags & 0x30) == 0)
{
pointsym = symbol(string(point.domain,":", point.name));
if (!.mappedPoints.contains(pointsym))
{
local PointName = string(pointsym);
.TimerEvery(01,`(#self).writeData(#PointName));
//.mappedPoints.add(pointsym, pointsym);
}
}
}
.prevcount = info.n_points;
}
}
method LogData.writeData(pointsymbol)
{
local row = new (.tableclass);
local pttime, ptltime;
local timestring;
local point;
// Generate a timestamp in database-independent format to the millisecond.
// Many databases strip the milliseconds from a timestamp, but it is harmless
// to provide them in case the database can store them.
point = PointMetadata(pointsymbol);
//princ(point,"\n");
if (point && number_p(point.value))
{
pttime = WindowsTimeToUnixTime(point.timestamp);
//princ(point,"\n");
ptltime = localtime(pttime);
//princ(ptltime,"\n");
if (!ptltime)
ptltime = localtime(0);
timestring = format("{'%04d-%02d-%02d %02d:%02d:%02d'}",
ptltime.year+1900, ptltime.mon+1, ptltime.mday, ptltime.hour, ptltime.min,
ptltime.sec);
//princ(timestring,"\n");
// Fill the row. Since we mapped the table into a Gamma class, we can
access
// the rows in the column as member variables of the mapped class.
row.ptname = string(pointsymbol);
row.ptvalue = point.value;
row.pttime = timestring;
// Perform the insertion. In this case we are providing no callback on
completion.
.thread.Insert(row, nil);
}
}
/* Write the 'main line' of the program here. */
method LogData.constructor ()
{
// Create and configure the database connection object
.thread = new ODBCThread();
.thread.Configure(.DSN, .username, .password, STORE_AND_FORWARD, .cachefile, 0);
// Query the table and map it to a class for each insertion. We want to run an
asynchronous event
// within the asynchronous initialization stage, so to do that we specify the
special method
// cbInitStage as the callback function of our asynchronous event
(GetTableInfo). We deal with
// the return from the GetTableInfo in the onSuccess argument of the init stage.
.thread.AddInitStage(`(#.thread).GetTableInfo("", "", (#.tablename),
"TABLE,VIEW",
`(#.thread).cbInitStage()),
`(#self).mapTable(#.tablename, SQLTables), nil);
//.thread.AddInitStage(`(#.thread).GetTableInfo("", "", (#.tablename),
"TABLE,VIEW",
// `(#self).mapTable(#.tablename, SQLTables)),
`(#.thread).cbInitStage(), nil);
// Do not start writing data to the table until we have successfully created and
mapped
// the table to a class. If we wanted to start writing data immediately, then
we would
// create the table class beforehand instead of querying the database for the
table
// definition. Then, even if the database were unavailable we could still cache
to the
// local disk until the database was ready.
.thread.AddInitStage(nil, `(#self).startLogging(), nil);
// Set up the callback functions for various events from the database thread
.thread.OnConnectionSucceeded = `(#self).onConnect();
.thread.OnConnectionFailed = `(#self).onConnectFail();
.thread.OnFileSystemError = `princ("File System Error: ", SQLResult, "\n");
.thread.OnODBCError = `princ("ODBC Error: ", SQLResult, "\n");
.thread.OnExecuteStored = nil;
.thread.Start();
// Create a menu item in the system tray that allows us to open a window to
monitor
// the performance of the ODBC thread. The menu strings can be edited as
desired.
.AddCustomSubMenu("ODBC Logging");
.AddCustomMenuItem("Monitor Performance",
`(#.thread).CreateMonitorWindow((#self), "ODBC Monitor"));
// Automatically update the point list every 1 seconds in case new points are v
added
// to the domain.
//.TimerEvery(01, `(#self).registerPoints());
}
/* Any code to be run when the program gets shut down. */
method LogData.destructor ()
{
if (instance_p(.thread))
destroy(.thread);
}
/* Start the program by instantiating the class. */
ApplicationSingleton (LogData);
Major parts of this Gamma script are the constructor,destructor,classes and methods. This program first initializes an ODBC connection using provided details and write each row of data using 'registerpoints' and 'writedata' methods. Please find additional details of each lines from comments in the program.
I've used Bugzilla for many years and one of my favourite features is the "request system" (http://www.bugzilla.org/features/#rs), also known as "flags". The issue can flagged (?) to a user; the user can then accept the request (+) or deny it (-).
I am in the process of re-evaluating our issue tracking tools and I can't seem to find anything other than Bugzilla that has this feature.
So I am wondering:
Does any other product offer similar functionality?
And if not, then is there a way to mimic it (using labels or custom fields or something)?
Your advice is appreciated (FYI: I am currently leaning towards YouTrack).
Alex V. asked for more details about Bugzilla's request system functionality. Here's an example:
An arbitrary list of flags can be created in the admin interface. When editing an issue, they are listed in a row, here's an example:
Next, someone can set the flag and ask for a followup. The screen shot shows me (dcherk) setting the DJiNNInput flag for john#doe.com:
Note that the same flag can be requested multiple times (not shown).
Later, john#doe.com might act on the flag in some way and mark the request as accepted:
Alternatively, john#doe.com might not be able to accept the request. In that case, he would deny it:
Needless to say, all these changes are tracked in the issue history, and can be searched and reported on.
In YouTrack there's no such functionality. What you can do, however, is you can Star the issue for a user as if the if the user did it themselves. They will then receive a notification about it and can either leave the star on the issue, or remove it.
Don't know exactly how those flags work in Bugzilla, so it's is probably not a complete replacement of that feature. If you elaborate on the desired behaviour, I'll let you know how (if possible) to completely mimic it.
FYI:
This is what we ended up doing in YouTrack:
Created two user[*] fields:
Input Requesting (i.e. the person requesting input)
Input Requested (i.e. the user whose input is needed)
The users can always set these fields manually, but we also added the following workflow rules to speed things up:
Rule 1, relates changes to the two field:
rule Input Requested
when Input Requested.changed {
if (Input Requested.isEmpty) {
Input Requesting.clear;
} else {
Input Requesting.add(loggedInUser);
}
}
Rule 2, closed issues do not need any more input:
rule Clear Input Requests When Issue Becomes Closed
when State.becomes({Closed}) {
Input Requested.clear;
Input Requesting.clear;
}
Rule 3, #mentioning sets the fields; replying clears the fields:
rule Input Requested via #mention
when comments.added.isNotEmpty {
var separators = " `!#%^&*()=[]{}:;'\"\\|,<>/?\n\r\t";
var mentionedUsers = "";
var myComment = comments.added.first;
var originalText = myComment.text;
var text = " " + originalText.lowerCase + " ";
var username = "";
var user = loggedInUser;
var index = -1;
index = text.indexOf("#", opts);
while (index != -1) {
index = index + 1;
username = "";
var nextSymbol = text.substring(index, index + 1);
while (!separators.contains(nextSymbol, opts)) {
username = username + nextSymbol;
index = index + 1;
nextSymbol = text.substring(index, index + 1);
}
if (username.endsWith(".", opts)) {
username = username.substringOfLength(username.length - 1, opts);
}
debug("Extracted #username: |" + username + "|");
if (username.isNotEmpty) {
user = project.getUser(username);
if (user != null && !mentionedUsers.contains("#" + user.login + ",", ignoreCase) && (user.isInGroup(permittedGroup.name) || permittedGroup == null || user == reporter) && (myComment.permittedGroup == null || user.isInGroup(myComment.permittedGroup.name))) {
if (Input Requesting.contains(user)) {
Input Requested.remove(loggedInUser);
if (Input Requested.isEmpty) {
Input Requesting.clear;
}
} else {
Input Requested.add(user);
Input Requesting.add(loggedInUser);
}
mentionedUsers = mentionedUsers + "#" + user.login + ",";
}
}
text = text.substringRelative("#" + username, pos: after);
index = text.indexOf("#", opts);
}
}
Hope that helps anyone along the way.
Is there anyway to know which user is calling the WCF Ria service on server side? Client side is siverlight, user has to be authenticated first in order to use the system.
I need to know which user is actually calling the service in my current task, thanks, i searched a lot, but seems no good findings.
Once the client side has successfully cleared your authentication challenge, the server can issue a token to the caller on the client side. In subsequent calls to the server, the client would send the token as one of the arguments and the server would verify the token and respond accordingly.
The token can contain a segment of information that identifies a given user, and implementing this will provide the functionality you are seeking.
The only guidelines for generating tokens is that they are unique, non-predictable and expirable. I have always encrypted my tokens so that they appear as gibberish, but step this is optional.
I've also done very much "googleing" and got a lot of headache before I got the solution.
I don't use RIA-Services - but it should be (hopefully) the same...:
The SL-Client sends a "login-request" to the server.
On the (WCF) server-side, I do the following (LoginData = Return-Info for SL-Client):
public LoginData LoginRequest() {
(...)
OperationContext context = OperationContext.Current;
System.ServiceModel.Channels.MessageProperties prp = context.IncomingMessageProperties;
System.ServiceModel.Channels.RemoteEndpointMessageProperty endPrp = prp[System.ServiceModel.Channels.RemoteEndpointMessageProperty.Name] as System.ServiceModel.Channels.RemoteEndpointMessageProperty;
(...)
try {
clientIP = endPrp.Address;
System.Net.IPAddress ipAddress = System.Net.IPAddress.Parse(clientIP);
System.Net.IPHostEntry ipHostEntry = System.Net.Dns.GetHostEntry(ipAddress);
(...)
If you want to check the users WindowsPrincipal, you can do the following (securityGroup = server-side setting, which users can login):
(...)
switch (securityGroup) {
case SecurityGroup.AllClientsCanAccess: {
clientCanLogin = true;
} break;
case SecurityGroup.UseWindowsCredentials: {
if (OperationContext.Current.ServiceSecurityContext != null && OperationContext.Current.ServiceSecurityContext.WindowsIdentity != null) {
if (OperationContext.Current.ServiceSecurityContext.WindowsIdentity.IsAuthenticated) {
if (subSecurityInfo1 == true) { // only clients in specific roles can log in
bool userRoleFound = false;
WindowsPrincipal userPrincipal = new WindowsPrincipal(OperationContext.Current.ServiceSecurityContext.WindowsIdentity);
if (userPrincipal == null)
break;
foreach (string userRoleToPass in subSecurityList) { // subSecurityList = settings, which roles can pass
loginError.ErrorInfo += string.Format("{0}\n", userRoleToPass);
if (userPrincipal.IsInRole(userRoleToPass)) {
clientCanLogin = userRoleFound = true;
break;
}
}
if (userRoleFound) {
loginError.ErrorInfo = string.Empty;
break;
}
else {
loginError.ErrorNo = LoginErrorCodeNoEnum.UserIsNotInRole;
}
}
(...)
Hope it helps...
I am looking for a Win32 API call to return the runtime context of my process. I want to be able to programmatically test if I am running as a service or am I running as standard application process.
Several ideas come to mind.... Since I always have service DAD.exe who runs SON.exe sometimes as his child and in service context --- and sometimes SON.exe is started not by DAD, and by a user.
SON.EXE would do API whoami() to learn which context he is running in.
Now DAD could create an environment var -- and then SON could test for this var -- and if found he knows he is a son of DAD and thus runnning as a service..... But this is weak...
Another idea would be to look at my SID or token and see if I could make this determination.... Again this looks at best more complex vs. a single API check...
The simple low-tech solution to this is to register your service to run with command line arguments that identify it as a service.
Another option is to use the Tool Help library. Using it, you take a snapshot of all the currently running processes and then you can walk through all the processes using the Process32First and Process32Next function. These return a structure (PROCESSENTRY32) that looks like:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;
as you walk through all the processes, as soon as you find the one whose th32ProcessID matches the one for SON.exe (see GetCurrentProcessId or GetProcessId ). If the th32ParentProcessID of that structure matches that of DAD.exe, then you know you were launched from DAD.exe.
Edit:
Answering your comment, I guess you could go one step further and then see who the parent of DAD.exe is, if it's services.exe, then you're a service.
Reading the documentation, I think you could determine whether you're in an interactive session or service via:
GetProcessWindowStation
GetUserObjectInformation(UOI_FLAGS)
and then WSF_VISIBLE should tell you.
If you wanted to differentiate between a logged-in user session and one that's inactive (Fast User Switching), I guess you could use GetThreadDesktop and GetUserObjectInformation(UOI_IO).
The best and simplest way to tell from inside the service is to set a flag when ServiceMain is called. But you're testing a child process, so see above.
I found the following:
bool WinUtil::IsServiceUser(HANDLE hToken, bool *is_service) {
if (is_service == NULL) {
return false;
}
TOKEN_STATISTICS ts;
DWORD dwSize = 0;
// Use token logon LUID instead of user SID, for brevity and safety
if (!::GetTokenInformation(hToken, TokenStatistics,
(LPVOID)&ts, sizeof(ts), &dwSize)) {
return false;
}
// Compare LUID
const LUID SystemLuid = SYSTEM_LUID;
const LUID LocalServiceLuid = LOCALSERVICE_LUID;
const LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
if (EqualLuid(SystemLuid, ts.AuthenticationId) ||
EqualLuid(LocalServiceLuid, ts.AuthenticationId) ||
EqualLuid(NetworkServiceLuid, ts.AuthenticationId)) {
*is_service = true;
return true;
}
// Not a service account
*is_service = false;
return true;
}
bool WinUtil::IsServiceProcess(bool *is_service) {
if (is_service == NULL) {
return false;
}
if (Util::IsVistaOrLater()) {
// Session 0 is dedicated to services
DWORD dwSessionId = 0;
if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId) ||
(dwSessionId == 0)) {
*is_service = true;
return true;
}
}
// Get process token
HANDLE hProcessToken = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(),
TOKEN_QUERY | TOKEN_QUERY_SOURCE,
&hProcessToken)) {
return false;
}
ScopedHandle process_token(hProcessToken);
// Process token is one for a service account.
if (!IsServiceUser(process_token.get(), is_service)) {
return false;
}
return true;
}
I think your looking for Topshelf http://topshelf-project.com/, it does the heavy lifting and makes it easier run as console or install as a service. Topshelf hosting application debugging in VS2010