Does the credential manager have a limit for the number of credentials stored? - winapi

I'm using CredWriteW to store some credentials and persisting through the user session. As we will have to store the credentials for lots of different accounts, I'm wondering: is there some kind of limit as to how many credentials can be stored on the credential manager?
I found this doc Credential limit per app | Microsoft Learn , but it's not clear whether it applies only to RDP, or to Credential Manager in general, or to something else. I've tried searching for this limit, but everything seems to point back to that same doc.
This is the code for reference:
CREDENTIAL credential = {0};
credential.Type = CRED_TYPE_DOMAIN_PASSWORD;
credential.TargetName = account;
credential.CredentialBlobSize = credentialBlobSize;
credential.CredentialBlob = (LPBYTE)password;
credential.Persist = CRED_PERSIST_SESSION;
credential.UserName = (LPWSTR)userName;
// Write the credential in the user space
if (!CredWriteW(&credential, 0))
{
// ...
}

This API indirectly mentioned/used in your RDP/Remote Deskop link is called "Vault", it's not the same API that the one used by CredWrite.
Vault is an undocumented API. See here for example on SO: Reverse engineering the function arguments of VaultRemoveItem or here on github's mimikatz
CredWrite is not documented to have any reasonable limit, here are 100 credentials I've just created with it:

Related

Is it possible to use signed-in windows user credentials to authenticate to web API?

I am implementing authentication for a command line client application that makes a web request to a web API. If I reason correctly, I can apply Azure Active Directory native app authentication scenario.
My concern here is that setting up Azure AD will require significant effort from the client app users on setting up AAD, plus they will have to work with an interactive dialog. This gets even worse in case no human is present, as the service to service scenario is even more complicated.
Is it possible to instead rely on the credentials of the signed-in user of the client computer? Assume Windows-based client machine that is joined to a domain, say FooDomain. The server uses an OWIN-based self-host implementation, Katana.
Related questions:
OWIN Web API Windows Service - Windows Identity Impersonation
#Konrad Jamrozik. IF you are working on .NET and want to use the logged-in user in Windows domain joined (your case), and even AAD joined, my advice would be to use MSAL.NET with the Integrated Windows Authentication (IWA) override. See https://aka.ms/msal-net-iwa. The simplified code looks like this:
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
var accounts = await app.GetAccountsAsync();
AuthenticationResult result=null;
if (accounts.Any())
{
result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
result = await app.AcquireTokenByIntegratedWindowsAuthAsync(scopes);
}
This sample explains how to register the app and provides all the details about the code: https://github.com/azure-samples/active-directory-dotnet-iwa-v2

Accessing GitLab via API with Ruby

Trying to access my university's GitLab for the first time via API (I'm the repo owner but have no access to the console and can not edit config files or restart the server), I seem to lack some mandatory basics. Tried to use Ruby (no rails) with the Narkoz API wrapper. Unfortunately, there are no tutorials available which explain the very first step:
How to connect to the server and authenticate using UID+passwd?
Can anybody explain the process in human-readable instructions? The GitLab Manual was not helpful for me.
I hope that I could then figure out how to add GitLab users to my repository. Don't want to add 100 users using the web interface.
The manual entry you linked is for signing into GitLab with a third-party OAuth provider, which doesn't sound like what you're trying to do. What it sounds like you're trying to do is request an OAuth token which you can then use to access GitLab's API.
From the documentation:
In this flow, a token is requested in exchange for the resource owner credentials (username and password). The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g. the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not available (such as an authorization code).
Which sounds like what you're trying to do.
One important thing of note from the documentation:
Deprecation notice: Starting in GitLab 8.11, the Resource Owner Password Credentials has been disabled for users with two-factor authentication turned on. These users can access the API using personal access tokens instead.
If this is the case for you, the following won't work and you'll need to generate an access token instead.
1. Requesting access token
POST request to /oauth/token with parameters:
{
"grant_type" : "password",
"username" : "user#example.com",
"password" : "secret"
}
Then, you'll receive the access token back in the response:
{
"access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
"token_type": "bearer",
"expires_in": 7200
}
You would then assign this token as your GitLab.private_token.
For the records -- what would have helped quicker is a minimum viable example -- especially since the API documentation contains known and not fixed errors.
host = myGitlabServer
require 'oauth2'
client = OAuth2::Client.new(clientIdFromGitLab, clientSecretFromGitLab, :site => host)
accessToken = client.password.get_token(myGitLabUid, myGitLabPassword)
require 'httparty'
# get own user data
response = HTTParty.get(host + '/api/v4/user?access_token=' + accessToken.token)
puts response.parsed_response
# get user data by user name
response = HTTParty.get(host + '/api/v4/users?username=' + username + '&access_token=' + access_token.token)
puts response.parsed_response[0]
# add user as reporter to project
response = HTTParty.post(host + '/api/v4/projects/' + projectId + '/members/?user_id=' + newUID + '&access_level=20&access_token=' + access_token.token)
puts response.parsed_response
or, you can use the excellent gitlab gem.
https://github.com/narkoz/gitlab

How can i use IAM in AWS to provide temporary security credentails

IAM has a component of service called Simple Token Service (STS). It allows you to create temporary access through SDK/ API to access AWS resources without having the need to create dedicated credentials. These STS tokens have a user-defined life period and those are destroyed post that. People use this service for accessing content from mobile devices such as Android/ IOS Apps.
But i don't know how to use this service.
Any help or support is appreciated.
Thanks
STS and IAM go hand in hand, and is real simple to use. Since you have not given a use case, please allow me to explain a few things before we get in coding.
Note: I code in PHP and the SDK version is 3.
The idea of STS is to create some tokens which allows the bearer to do certain actions without you (the owner or the grantee) compromising your own credentials. Which type of STS you are going to use depends on what you want to do. Possible actions are listed here.
E.g.1: Typically, you use AssumeRole for cross-account access or
federation. Imagine that you own multiple accounts and need to access
resources in each account. You could create long-term credentials in
each account to access those resources. However, managing all those
credentials and remembering which one can access which account can be
time consuming. Instead, you can create one set of long-term
credentials in one account and then use temporary security credentials
to access all the other accounts by assuming roles in those accounts.
E.g.2: Typically, you use GetSessionToken if you want to use MFA to protect
programmatic calls to specific AWS APIs like Amazon EC2 StopInstances.
Let us assume you have an IAM user and you want to create many temporary credentials for that user, each credential with a time frame of 15 minutes. Then you will write the following code:
$stsClient = \Aws\Laravel\AwsFacade::createClient('sts', array(
'region' => 'us-east-1'
));
$awsTempCreds = $stsClient->getSessionToken(array(
'DurationSeconds' => 900
));
Points to note:
Credentials that are created by IAM users are valid for the duration that you specify, from 900 seconds (15 minutes) up to a maximum of 129600 seconds (36 hours), with a default of 43200 seconds (12 hours); credentials that are created by using account credentials can range from 900 seconds (15 minutes) up to a maximum of 3600 seconds (1 hour), with a default of 1 hour.
In the above example I am getting $stsClient using AWS Facade which is part of Laravel framework. It is up to you how you get hold of $stsClient by passing credentials. Read this installation guide on how to instantiate your $stsClient.
Since STS is a global resource i.e. it does not require you to be in a specific region, you MUST ALWAYS set the region to us-east-1. If your region is set to anything else, you will get errors like should be scoped to a valid region, not 'us-west-1'.
There is no limit on how many temporary credentials you make.
These credentials will have the SAME permissions from which Account/IAM they are derived from.
Above code returns a set of temporary credentials for an AWS account or IAM user. The credentials consist of an access key ID, a secret access key, and a security token, plus a few other information such as expiry time.
You can now give these temporary credentials to someone else. Let us say I gave this to my friend who happens to use the Javascript API. He now can write codes like this:
<script>
var accessKey = '<?php echo $credentials["AccessKeyId"] ?>';
var accessToken = '<?php echo $credentials["SecretAccessKey"] ?>';
var accessSessionToken = '<?php echo $credentials["SessionToken"] ?>';
AWS.config.update({
credentials: {
accessKeyId: accessKey,
secretAccessKey: accessToken,
sessionToken: accessSessionToken
},
region: 'us-east-1'
});
var bucket = new AWS.S3();
var file = fileChooser.files[0];
var params = {Bucket: 'mybucket', Key: file.name, ContentType: file.type, Body: file};
bucket.upload(params, function (err, data) {
results.innerHTML = err ? 'ERROR!' : 'UPLOADED.';
}).on('httpUploadProgress', function(evt) {
console.log('Progress:', evt.loaded, '/', evt.total);
});
</script>
What this script does?
Creates a new client using temporary credentials.
Uploads a very large file (more than 100MB) to mybucket using Multipart Upload.
Similarly, you can do operations on any AWS resource as long as you temporary credentials have permission to do so. Hope this helps.
STS is little tough to understand (need to put real time in reading it). I will try (yes try!) to explain as simple as possible. The service is useful if you need to do things like this:
You have a bucket on S3. You have say 100 users who you would like to upload files to this bucket. It is obvious that you should not distribute your AWS key/secret to all of them. Here comes STS. You can use STS to allow these users to write to a "portion" (say a folder by their google-id) of the bucket for a "limited time" (say 1 hr). You achieve this by doing the required setup (IAM, S3 policy and STS-AssumeRoles) and sending them a URL (which they use to upload). In this case, you can also use the Web Identity Federation to authenticate users by Google/FB/Amazon.com. So with no backend code, this workflow is achievable. Web Identity Federation Playground gives you a sense of how this works.
You have an AWS account and want somebody else (another AWS user) to help you manage this. Here again you give the other user limited-time access to a selected portion of your AWS resource without sharing the Key/secret
Assume you have a DynamoDB setup with a row of data per your app-user. Here you need to ensure a given user can write only on his row of data and not others. You can use STS to setup stuff like this.
A complete example is here:
Web Identity Federation with Mobile Applications : Articles & Tutorials : Amazon Web Services : http://aws.amazon.com/articles/4617974389850313
More reading:
Creating Temporary Security Credentials for Mobile Apps Using Identity Providers - AWS Security Token Service : http://docs.aws.amazon.com/STS/latest/UsingSTS/CreatingWIF.html

Google Group Settings API enabled for service accounts?

Most of the Google Management APIs seem to have been enabled for Service Accounts. For example, I can retrieve calendars like so:
string scope = Google.Apis.Calendar.v3.CalendarService.Scopes.Calendar.ToString().ToLower();
string scope_url = "https://www.googleapis.com/auth/" + scope;
string client_id = "999...#developer.gserviceaccount.com";
string key_file = #"\path\to\my-privatekey.p12";
string key_pass = "notasecret";
AuthorizationServerDescription desc = GoogleAuthenticationServer.Description;
X509Certificate2 key = new X509Certificate2(key_file, key_pass, X509KeyStorageFlags.Exportable);
AssertionFlowClient client = new AssertionFlowClient(desc, key) { ServiceAccountId = client_id, Scope = scope_url };
OAuth2Authenticator<AssertionFlowClient> auth = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
CalendarService service = new CalendarService(auth);
var x = service.Calendars.Get("calendarID#mydomain.com").Fetch();
However, identical code on the GroupssettingsService returns a 503 - Server Not Available. Does that mean service accounts can't be used with that API?
In a possibly related issue, the scope of the Groups Settings Service seems to be apps.groups.settings but if you call
GroupssettingsService.Scopes.AppsGroupsSettings.ToString().ToLower();
...you get appsgroupssettings instead, without the embedded periods.
Is there another method to use service accounts for the GroupssettingsService? Or any information on the correct scope string?
Many thanks.
I found this thread, and the most important part of the docs after some time. Posting so others don't waste their time in the future.
Your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported. If your application uses Google Sign-In, some aspects of authorization are handled for you.
See the "About authorization protocols" section of the docs
Why do you need to use a service account for this? You can use regular OAuth 2.0 authorization flows to get an authorization token from a Google Apps super admin user and use that:
https://developers.google.com/accounts/docs/OAuth2InstalledApp

Why doesn't LogonUser(...) work for domain accounts?

I've been trying to use LogonUser(...) to get an access token for a user account, as in this MSDN sample.
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
When I run the sample (with Administrator privileges) it works fine when given a domain of . and a local user account name and password, but no matter what I do I get error code 1326 (Logon failure: unknown user name or bad password) if I try to use a domain account. I get the same result if I enter garbage for the domain, which makes me wonder if it's actually contacting the DC at all.
What could be stopping this from working?
In my case the issue, similar to the question asker, was that the account I was trying to authenticate to was in a domain that my current machine did not belong to. Unlike the original poster, my machine should not and could not be part of this other domain. I wanted the login to perform action on a resource on this domain though.
The answer was the following
bool success = LogonUser(
userName,
domain,
password,
(int)LOGON32_LOGON_NEW_CREDENTIALS, //9
(int)LOGON32_PROVIDER_DEFAULT, //0
out userToken);
with the following constants defined:
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
public const int LOGON32_PROVIDER_DEFAULT = 0;
Hopefully this will help others who are lost in a similar situation.
Edit: As mentioned in the comments below, this logon type allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identifier but uses different credentials for other network connections. As a result of that fact, "success" will return true even if the password is bad. You will need an additional check beyond "success" to confirm that the credentials are actually good.
This was not a concern in my initial use case as we used the current network user's credential in another function to pull the plaintext password from secure storage. So it would have never been wrong unless there was an inconsistency between that system and active directory in which case we had bigger problems.
In my case it was the fact that, although I was logged in to my computer as a domain user, my computer was not itself part of the domain. Once added to the domain the sample started to work.
Use DOMAIN\LOGIN with an empty domainname for that case...

Resources