2009-05-04 3 views
14

Voici ma pile de sécurité Windows/.NET:Comment démarrer/arrêter un service Windows à partir d'une application ASP.NET - Problèmes de sécurité

  • Un service Windows en cours d'exécution en tant que LocalSystem sur un Windows Server 2003 boîte.
  • Un site Web .NET 3.5 en cours d'exécution sur la même case, sous serveur de production « par défaut » paramètres IIS (donc probablement en tant qu'utilisateur NETWORKSERVICE?)

Sur mon environnement par défaut VS2008 DEV Je cette seule méthode, qui obtient appelé à partir de l'application ASP.NET, qui fonctionne très bien:

private static void StopStartReminderService() { 

    ServiceController svcController = new ServiceController("eTimeSheetReminderService"); 

    if (svcController != null) { 
     try { 
      svcController.Stop(); 
      svcController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10)); 
      svcController.Start(); 
     } catch (Exception ex) { 
      General.ErrorHandling.LogError(ex); 
     } 
    } 
} 

Quand j'exécuter sur le serveur de production, je reçois l'erreur suivante de la ServiceController:

Sourc e: System.ServiceProcess -> System.ServiceProcess.ServiceController -> IntPtr GetServiceHandle (Int32) -> System.InvalidOperationException Message: Impossible d'ouvrir le service eTimeSheetReminderService sur l'ordinateur '.'.

Pourquoi cela se produit-il et comment puis-je y remédier?

EDIT:

La réponse est ci-dessous, la plupart du temps dans les commentaires, mais de préciser:

  1. La question était liée à la sécurité, et eu lieu parce que le compte NETWORKSERVICE n'a pas eu suffisamment de droits Démarrer/Arrêter un service
  2. J'ai créé un compte d'utilisateur local et l'ai ajouté au groupe PowerUsers (ce groupe a presque les droits d'administrateur)
  3. Je ne veux pas toute mon application Web pour usurper l'identité de cet utilisateur tout le temps, donc j'emprunte l'identité seulement dans la méthode où je manipule le service. Je le fais en utilisant les ressources suivantes pour me aider à le faire dans le code:

MS KB article et this, just to get a better understanding

REMARQUE: Je n'usurper l'identité non pas par le web.config, je le fais dans le code. Voir l'article MS KB ci-dessus.

+0

De quel type d'exception s'agissait-il? Une 'System.InvalidOperationException'? – Phaedrus

+0

@Phaedrus: Hé, ouais, ouais c'était. J'ai mis plus d'informations sur les erreurs dans mon édition. acclame – andy

Répondre

6

Essayez d'ajouter ceci à votre Web.Config.

<identity impersonate="true"/> 
+0

hey phaedrus, même erreur – andy

+0

L'accès anonyme est-il activé dans IIS? – Phaedrus

+0

Oui, "Activer l'accès anonyme" est coché – andy

0

Juste un pressentiment, mais il ne me semble pas que l'erreur est nécessairement liée à la sécurité. Avez-vous donné le même nom au service sur le serveur de production?

+0

@cdonner: oui, je m'en doutais aussi, mais je ne savais pas comment je pouvais le tester? Oui, le nom est le même, il est défini dans le code, dans le composant ServiceInstaller. Des idées sur la façon dont je pourrais obtenir une réponse définitive à savoir si c'est même la sécurité? acclame – andy

+0

pouvez-vous démarrer et arrêter à partir de la ligne de commande, en utilisant NET START/STOP? – cdonner

+0

hey man, ouais, en cours d'exécution "NET START eTimeSheetReminderService" dans l'invite de commande démarre le service avec succès – andy

12

Pour donner la permission IIS pour démarrer/arrêter un service particulier:

  • Téléchargez et installez Subinacl.exe. ( Assurez-vous d'obtenir la dernière version! Les versions antérieures distribuées dans certains kits de ressources ne fonctionnent pas!)
  • Émettre une commande similaire à: subinacl /service {yourServiceName} /grant=IIS_WPG=F

Cette accorde tous les droits de contrôle de service pour ce service particulier au groupe IIS_WPG intégré. (Cela fonctionne pour IIS6/Win2k3.) YMMV pour les nouvelles versions de IIS)

+0

Parfait, a fait l'affaire pour moi et je n'ai même pas besoin d'ajouter une usurpation d'identité à mon web.config. À votre santé! – Nabster

+0

C'est la meilleure solution à mon avis. Merci Martin_ATS. – amiir

+0

Utilisez cet outil pour octroyer des droits à un utilisateur, en utilisant l'emprunt d'identité pour usurper l'identité de l'utilisateur qui a le droit de démarrer et d'arrêter le service. – 130nk3r5

1

Ce fut une bonne question qui m'a intriguée et ...

Voici donc ce que je l'ai fait pour résoudre ce problème.

  • Étape 1: créez un compte d'utilisateur Windows sur la machine locale avec des droits minimaux.
  • Étape 2: Donnez à ce droits de l'utilisateur pour démarrer et arrêter le service via subinacl.exe
  • c.-à-subinacl.exe/Service WindowsServiceName/GRANT = PCNAME \ TestUser = STOE
  • Dowload de: http://www.microsoft.com/en-za/download/details.aspx?id=23510
  • Étape 3: Utilisez Impersonation pour usurper l'identité de l'utilisation créé à l'étape 1 pour démarrer et arrêter le service

    public const int LOGON32_PROVIDER_DEFAULT = 0; 
    
    WindowsImpersonationContext _impersonationContext; 
    
    [DllImport("advapi32.dll")] 
    // ReSharper disable once MemberCanBePrivate.Global 
    public static extern int LogonUserA(String lpszUserName, 
        String lpszDomain, 
        String lpszPassword, 
        int dwLogonType, 
        int dwLogonProvider, 
        ref IntPtr phToken); 
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    // ReSharper disable once MemberCanBePrivate.Global 
    public static extern int DuplicateToken(IntPtr hToken, 
        int impersonationLevel, 
        ref IntPtr hNewToken); 
    
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    // ReSharper disable once MemberCanBePrivate.Global 
    public static extern bool RevertToSelf(); 
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    // ReSharper disable once MemberCanBePrivate.Global 
    public static extern bool CloseHandle(IntPtr handle); 
    
    private bool _impersonate; 
    
    public bool ImpersonateValidUser(String userName, String domain, String password) 
    { 
        IntPtr token = IntPtr.Zero; 
        IntPtr tokenDuplicate = IntPtr.Zero; 
    
        if (RevertToSelf()) 
        { 
         if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, 
          LOGON32_PROVIDER_DEFAULT, ref token) != 0) 
         { 
          if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
          { 
           var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
           _impersonationContext = tempWindowsIdentity.Impersonate(); 
           if (_impersonationContext != null) 
           { 
            CloseHandle(token); 
            CloseHandle(tokenDuplicate); 
            _impersonate = true; 
            return true; 
           } 
          } 
         } 
        } 
        if (token != IntPtr.Zero) 
         CloseHandle(token); 
        if (tokenDuplicate != IntPtr.Zero) 
         CloseHandle(tokenDuplicate); 
        _impersonate = false; 
        return false; 
    } 
    
    #region Implementation of IDisposable 
    
    
    
    
    #endregion 
    
    #region Implementation of IDisposable 
    
    private void Dispose(bool dispose) 
    { 
        if (dispose) 
        { 
         if (_impersonate) 
          _impersonationContext.Undo(); 
         _impersonationContext.Dispose(); 
        } 
    } 
    
    public void Dispose() 
    { 
        Dispose(true); 
    } 
    #endregion 
    
    public static void StartStopService(bool startService, string serviceName) 
    { 
        using (var impersonateClass = new Impersonation()) 
        { 
         impersonateClass.ImpersonateValidUser(Settings.Default.LocalUsername, Settings.Default.Domain, Settings.Default.Password); 
         using (var sc = new ServiceController(serviceName)) 
         { 
          if (startService) 
           sc.Start(); 
          else if (sc.CanStop) 
           sc.Stop(); 
         } 
    
        } 
    } 
    
+0

LOGON32_LOGON_INTERACTIVE n'a pas fonctionné pour moi mais grâce à cette réponse https://stackoverflow.com/a/28503968/4568373 je l'ai changé à LOGON32_LOGON_NETWORK et a bien fonctionné. –

0

Si votre application Web a la base de données Windows Service peut y accéder, vous pouvez simplement utiliser l'indicateur dans la base de données pour redémarrer le service. Dans le service, vous pouvez lire ce drapeau et redémarrer s'il n'est pas occupé, etc. Seulement si vous pouvez modifier le code du service. S'il s'agit d'un service tiers, vous pouvez créer votre propre service Windows et utiliser la configuration de base de données pour contrôler (redémarrer) les services. C'est la manière sûre et vous donne beaucoup plus de flexibilité et de sécurité.

1

Mise à jour pour IIS 8 (et peut-être certaines versions un peu plus tôt)

Le groupe d'utilisateurs IIS_WPG n'existe plus. Changé pour IIS_IUSRS.

De même, pour commencer à arrêter un service, il n'est pas nécessaire de donner des autorisations complètes (F). Les autorisations pour démarrer, arrêter et mettre en pause un service (TOP) devraient suffire. En tant que tel la commande doit être:

subinacl/service {yourServiceName}/grant = IIS_IUSRS = TOP

Notez que vous devez pointer l'invite de commande (de préférence élevé à exécuter en tant qu'administrateur) à C:\Windows\System32 Dossier avant d'exécuter cette commande.

Vérifiez également que vous avez copié le fichier subinacl.exe à C:\Windows\System32 à partir du répertoire d'installation s'il y a une erreur.

Questions connexes