public static YubicoResponse Validate(List <string> urls, string userAgent) { List <Task <YubicoResponse> > tasks = new List <Task <YubicoResponse> >(); CancellationTokenSource cancellation = new CancellationTokenSource(); foreach (string url in urls) { Task <YubicoResponse> task = new Task <YubicoResponse>((o) => { return(Verify((string)o, userAgent)); }, url, cancellation.Token); tasks.Add(task); task.Start(); } YubicoResponse response = null; AggregateException taskException = null; while (tasks.Count != 0) { int completed = Task.WaitAny(tasks.ToArray()); Task <YubicoResponse> task = tasks[completed]; tasks.Remove(task); try { if (task.Result != null) { if (response == null || (task.Result.GetStatus() == YubicoResponseStatus.OK)) { response = task.Result; } cancellation.Cancel(); } } catch (AggregateException ex) { taskException = ex; } } if (response == null && taskException != null) { throw taskException.Flatten(); } return(response); }
protected override bool ValidateUserNameCredential(string userName, string password, out List <Claim> claims) { claims = new List <Claim>(); int otpLength = 32 + _config.YubicoSettings.PublicIdLength; if (password.Length >= otpLength) { string otp = password.Substring(password.Length - otpLength, otpLength); if (YubicoClient.IsOtpValidFormat(otp)) { string windowsPassword = password.Substring(0, password.Length - otpLength); List <Claim> windowsClaims; if (base.ValidateUserNameCredential(userName, windowsPassword, out windowsClaims)) { using (PrincipalContext context = GetContext(userName, windowsPassword)) { using (Principal principal = Principal.FindByIdentity(context, userName)) { using (DirectoryEntry directoryEntry = principal.GetUnderlyingObject() as DirectoryEntry) { string publicId = null; if (directoryEntry.Properties.Contains(_config.YubicoSettings.PublicIdAttributeName)) { publicId = directoryEntry.Properties[_config.YubicoSettings.PublicIdAttributeName].Value.ToString(); } if (!string.IsNullOrEmpty(publicId)) { YubicoClient client = new YubicoClient(_config.YubicoSettings.ClientId, _config.YubicoSettings.APIKey); client.SetUrls(_apiUrls); YubicoResponse response = client.Validate(otp); if (response != null) { if (response.GetStatus() == YubicoResponseStatus.OK && response.GetPublicId() == publicId) { claims.AddRange(windowsClaims); return(true); } } } else { LogEntry entry = new LogEntry(); entry.Severity = TraceEventType.Error; entry.Priority = -1; if (Logger.ShouldLog(entry)) { entry.Message = string.Format("Unable to find the user's PublicId. PublicIdAttributeName=\"{0}\".", _config.YubicoSettings.PublicIdAttributeName); Logger.Write(entry); } } } } } } } else { LogEntry entry = new LogEntry(); entry.Severity = TraceEventType.Error; entry.Priority = -1; if (Logger.ShouldLog(entry)) { entry.Message = "Invalid OTP Format."; Logger.Write(entry); } } } else { LogEntry entry = new LogEntry(); entry.Severity = TraceEventType.Error; entry.Priority = -1; if (Logger.ShouldLog(entry)) { entry.Message = "Invalid OTP Length."; Logger.Write(entry); } } return(false); }
/// <summary> /// Do verification of OTP /// </summary> /// <param name="otp">The OTP from a YubiKey in modhex</param> /// <returns>YubicoResponse indicating status of the request</returns> /// <exception cref="YubicoValidationFailure"/> /// <exception cref="FormatException"/> public YubicoResponse Validate(string otp) { if (!IsOtpValidFormat(otp)) { throw new YubicoValidationException("Bad OTP format."); } if (_nonce == null) { _nonce = GenerateNonce(); } SortedDictionary <string, string> queryMap = new SortedDictionary <string, string>(); queryMap.Add("id", _clientId); queryMap.Add("nonce", _nonce); queryMap.Add("otp", otp); queryMap.Add("timestamp", "1"); if (_sync != null) { queryMap.Add("sl", _sync); } string query = null; foreach (KeyValuePair <string, string> pair in queryMap) { if (query == null) { query = ""; } else { query += "&"; } query += pair.Key + "=" + Uri.EscapeDataString(pair.Value); } if (_apiKey != null) { query += "&h=" + Uri.EscapeDataString(DoSignature(query, _apiKey)); } List <string> urls = new List <string>(); foreach (string url in _apiUrls) { urls.Add(url + "?" + query); } try { YubicoResponse response = YubicoValidate.Validate(urls, _userAgent); if (_apiKey != null && response.GetStatus() != YubicoResponseStatus.BAD_SIGNATURE) { string responseString = null; string serverSignature = null; foreach (KeyValuePair <string, string> pair in response.GetResponseMap()) { if (pair.Key.Equals("h")) { serverSignature = pair.Value; } else { if (responseString == null) { responseString = ""; } else { responseString += "&"; } responseString += pair.Key + "=" + pair.Value; } } string clientSignature = DoSignature(responseString, _apiKey); if (serverSignature == null || !clientSignature.Equals(serverSignature)) { throw new YubicoValidationException("Server signature did not match the key."); } } if (response.GetStatus() == YubicoResponseStatus.OK) { if (!response.GetNonce().Equals(_nonce)) { throw new YubicoValidationException("Nonce in request and response does not match."); } else if (!response.GetOtp().Equals(otp)) { throw new YubicoValidationException("OTP in request and response does not match."); } return(response); } throw new YubicoValidationException(string.Format("OTP validation failed: {0}", response.GetStatus())); } finally { // set nonce to null so we will generate a new one for the next request _nonce = null; } }