Google-api-dotnet-client: Library doesn't work from sandboxed low-integrity process

Created on 28 Apr 2020  路  6Comments  路  Source: googleapis/google-api-dotnet-client

I'm experiencing problems using the Google Client libraries for .Net from a sandboxed low-integrity process. I'm suspecting that the problem is caused by the library attempting to write to C:\Users\<username>\AppData\Roaming\Google.Apis.Auth, which is read-only for low-integrity processes.

Suggested fixes:

  • Either, do not write to AppData\Roaming if running in low-integrity process (only store in-memory),
  • or, write configuration files to AppData\LocalLow if running in low-integrity process.

Steps to reproduce

  • Build https://github.com/youtube/api-samples/blob/master/dotnet/UploadVideo.cs into UploadVideo.exe
  • From a command prompt with admin permissions, run "icacls UploadVideo.exe /setintegritylevel Low" so that the executable will run in low integrity.
  • From a regular command prompt, run "UploadVideo.exe".
  • Observe: "Error: Operation is not supported on this platform."

Environment:

  • Windows 10 1909 (x64).
  • Google.Apis version="1.45.0" targetFramework="net472"
  • Google.Apis.YouTube.v3 version="1.45.0.1929" targetFramework="net472"

Process explorer can be used to check if a process is running under low, medium or high integrity.

investigating question

All 6 comments

I'll have a look in the morning, but I suspect this is due to the sample using a file-based credential cache.

I've just tested to explicitly use NullDataStore in the sample. Unfortunately, it doesn't seem to help.

Okay, stack trace from the sample app, having changed the code to print e rather than e.Message:

Error: System.UnauthorizedAccessException: Access to the path 'C:\Users\[...]\AppData\Roaming\Google.Apis.Auth' is denied.
   at Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
   at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.<AuthorizeAsync>d__1.MoveNext() in c:\code\google.com\google-
api-dotnet-client\default\Tools\Google.Apis.Release\bin\Debug\test\default\Src\GoogleApis.Auth.DotNet4\OAuth2\GoogleWebA
uthorizationBroker.cs:line 59
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Google.Apis.YouTube.Samples.UploadVideo.<Run>d__1.MoveNext() in C:\[...]\UploadVideo.cs:line 67

Will create a smaller sample app so I can look into it more easily.

TL;DR: The library works fine, but you need to avoid the process having to:

  • Write credentials to a file store
  • Launch a local web server to listen for codes
  • Launch a browser process

I've just tested to explicitly use NullDataStore in the sample. Unfortunately, it doesn't seem to help.

I've just tested that, and it does help, or rather it at least makes a difference: it changes the nature of the error. It no longer tries to write to a file, but instead fails when it can't construct an HttpListener - which is pretty reasonable in a low-integrity process.
To avoid it having to create a web server to listen for the code being passed back from the browser, you can pass a PromptCodeReceiver to AuthorizeAsync too. At that point, it doesn't crash - but it doesn't launch the browser either, presumably for exactly the same reason. (Process.Start doesn't throw an exception, which is a little odd.)

You can get round that problem by building your own ICodeReceiver based on PromptCodeReceiver, like this:

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Issue1551
{
    public class ConsoleOnlyCodeReceiver : ICodeReceiver
    {
        public string RedirectUri => GoogleAuthConsts.InstalledAppRedirectUri;

        public Task<AuthorizationCodeResponseUrl> ReceiveCodeAsync(AuthorizationCodeRequestUrl url,
            CancellationToken taskCancellationToken)
        {
            var authorizationUrl = url.Build().AbsoluteUri;
            Console.WriteLine("Please visit the following URL in a web browser, then enter the code shown after authorization:");
            Console.WriteLine(authorizationUrl);
            Console.WriteLine();

            string code = string.Empty;
            while (string.IsNullOrEmpty(code))
            {
                Console.WriteLine("Please enter code: ");
                code = Console.ReadLine();
            }
            return Task.FromResult(new AuthorizationCodeResponseUrl { Code = code });
        }
    }
}

That is able to authorize the user. I haven't tried using the YouTube API, but I was able to use the credentials to list the storage buckets for my Cloud Project, so clearly the library is able to make requests and get responses.

The user experience is pretty awful at that point (copying and pasting URLs into a browser) but that's just a fact of life when we don't have the normal facilities available. If you're able to authorize in a separate application first and store the access/refresh tokens somewhere that your application can get at them, that will avoid the problems you're facing.

Thanks a lot for _very_ helpful feedback @jskeet . It's good to see that the existing design already has extension points that allow for working around low-integrity limitations.

Web API's aren't my primary occupation, so I'll need a few days to digest your feedback.

Okay - I'll close the issue for now as I believe it's been addressed as far as we really can - but if there are alternatives you'd like to propose, or if the code receiver I've provided doesn't work for you, please add another comment and I'll reopen the issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

felinepc picture felinepc  路  4Comments

riyadparvez picture riyadparvez  路  9Comments

NKnusperer picture NKnusperer  路  6Comments

jskeet picture jskeet  路  6Comments

AlKau picture AlKau  路  6Comments