C ++ – Comment définir les permissions de fichiers (multiplate-forme)

J’utilise C ++ ofstream pour écrire un fichier. Je veux définir les permissions pour être uniquement accessible par l’utilisateur: 700. Dans unix; Je suppose que je peux juste émettre un system("chmod 700 file.txt"); mais j’ai aussi besoin de ce code pour travailler sur Windows. Je peux utiliser des api Windows; mais quelle est la meilleure façon de faire de la plate-forme croisée c ++?

Ironiquement, je viens juste de rencontrer ce même besoin plus tôt aujourd’hui.

Dans mon cas, la réponse a été à quel niveau de granularité de permission j’ai besoin sous Windows, par rapport à Linux. Dans mon cas, je ne m’occupe que des permissions utilisateur, groupe et autres sur Linux. Sous Windows, les permissions de lecture / écriture de base de DOS sont suffisantes pour moi, c’est-à-dire que je n’ai pas besoin de traiter ACL sous Windows.

En règle générale, Windows dispose de deux modèles de privilège: le modèle DOS de base et le nouveau modèle de contrôle d’access. Sous le modèle DOS, il existe un type de privilège: le droit d’écriture. Tous les fichiers peuvent être lus, il n’y a donc aucun moyen de désactiver l’autorisation de lecture (car elle n’existe pas). Il n’y a pas non plus de concept de permission d’exécution. Si un fichier peut être lu (la réponse est oui) et qu’il est binary, alors il peut être exécuté; sinon il ne peut pas.

Le modèle DOS de base est suffisant pour la plupart des environnements Windows, c.-à-d. Les environnements où le système est utilisé par un seul utilisateur dans un emplacement physique pouvant être considéré comme relativement sécurisé. Le modèle de contrôle d’access est plus complexe de plusieurs ordres de grandeur.

Le modèle de contrôle d’access utilise des listes de contrôle d’access (ACL) pour accorder des privilèges. Les privilèges ne peuvent être accordés que par un processus doté des privilèges nécessaires. Ce modèle permet non seulement le contrôle des utilisateurs, groupes et autres avec l’autorisation de lecture, écriture et exécution, mais il permet également de contrôler les fichiers sur le réseau et entre les domaines Windows. (Vous pouvez également obtenir ce niveau de folie sur les systèmes Unix avec PAM.)

Remarque: Le modèle de contrôle d’access n’est disponible que sur les partitions NTFS. Si vous utilisez des partitions FAT, vous êtes SOL.

Utiliser ACL est une grande douleur dans le cul. Ce n’est pas une tâche sortingviale et vous devrez apprendre non seulement la liste de contrôle d’access, mais aussi tous les descripteurs de sécurité, les jetons d’access et de nombreux autres concepts de sécurité Windows avancés.

Heureusement pour moi, pour mes besoins actuels, je n’ai pas besoin de la véritable sécurité offerte par le modèle de contrôle d’access. Je peux me débrouiller en faisant semblant de définir des permissions sur Windows, à condition de définir des permissions sur Linux.

Windows prend en charge ce qu’ils appellent une version “conforme à ISO C ++” de chmod (2). Cette API s’appelle _chmod, et est similaire à chmod (2), mais plus limitée et non compatible avec le type ou le nom (bien sûr). Windows a également un chmod obsolète, vous ne pouvez donc pas simplement append chmod à Windows et utiliser le chmod (2) sous Linux.

J’ai écrit ce qui suit:

 #include  #include  #ifdef _WIN32 # include  typedef int mode_t; /// @Note If STRICT_UGO_PERMISSIONS is not defined, then setting Read for any /// of User, Group, or Other will set Read for User and setting Write /// will set Write for User. Otherwise, Read and Write for Group and /// Other are ignored. /// /// @Note For the POSIX modes that do not have a Windows equivalent, the modes /// defined here use the POSIX values left shifted 16 bits. static const mode_t S_ISUID = 0x08000000; ///< does nothing static const mode_t S_ISGID = 0x04000000; ///< does nothing static const mode_t S_ISVTX = 0x02000000; ///< does nothing static const mode_t S_IRUSR = mode_t(_S_IREAD); ///< read by user static const mode_t S_IWUSR = mode_t(_S_IWRITE); ///< write by user static const mode_t S_IXUSR = 0x00400000; ///< does nothing # ifndef STRICT_UGO_PERMISSIONS static const mode_t S_IRGRP = mode_t(_S_IREAD); ///< read by *USER* static const mode_t S_IWGRP = mode_t(_S_IWRITE); ///< write by *USER* static const mode_t S_IXGRP = 0x00080000; ///< does nothing static const mode_t S_IROTH = mode_t(_S_IREAD); ///< read by *USER* static const mode_t S_IWOTH = mode_t(_S_IWRITE); ///< write by *USER* static const mode_t S_IXOTH = 0x00010000; ///< does nothing # else static const mode_t S_IRGRP = 0x00200000; ///< does nothing static const mode_t S_IWGRP = 0x00100000; ///< does nothing static const mode_t S_IXGRP = 0x00080000; ///< does nothing static const mode_t S_IROTH = 0x00040000; ///< does nothing static const mode_t S_IWOTH = 0x00020000; ///< does nothing static const mode_t S_IXOTH = 0x00010000; ///< does nothing # endif static const mode_t MS_MODE_MASK = 0x0000ffff; ///< low word static inline int my_chmod(const char * path, mode_t mode) { int result = _chmod(path, (mode & MS_MODE_MASK)); if (result != 0) { result = errno; } return (result); } #else static inline int my_chmod(const char * path, mode_t mode) { int result = chmod(path, mode); if (result != 0) { result = errno; } return (result); } #endif 

Il est important de se rappeler que ma solution ne fournit qu'une sécurité de type DOS. Ceci est également connu comme aucune sécurité, mais c'est la quantité de sécurité que la plupart des applications vous offrent sous Windows.

De plus, sous ma solution, si vous ne définissez pas STRICT_UGO_PERMISSIONS, lorsque vous autorisez un groupe ou un autre (ou que vous le supprimez), vous modifiez réellement le propriétaire. Si vous ne souhaitez pas le faire, mais que vous n'avez toujours pas besoin d'permissions Windows complètes, définissez simplement STRICT_UGO_PERMISSIONS.

Il n’y a pas de moyen multi-plateforme pour faire cela. Windows ne prend pas en charge les permissions de fichiers de style Unix. Pour faire ce que vous voulez, vous devrez créer une liste de contrôle d’access à ce fichier, ce qui vous permettra de définir explicitement les permissions d’access pour les utilisateurs et les groupes.

Une alternative pourrait être de créer le fichier dans un répertoire dont les parameters de sécurité ont déjà été définis pour exclure tout le monde sauf l’utilisateur.

L’appel system() est une bête étrange. J’ai été mordu par une implémentation du système NOP () sur un Mac il y a plusieurs mois. Son implémentation définie signifie que la norme ne définit pas ce qu’une implémentation (plateforme / compilateur) est supposée faire. Malheureusement, il s’agit également de la seule manière standard de faire quelque chose en dehors du cadre de votre fonction (dans votre cas – changer les permissions).

Mise à jour: un hack proposé:

  • Créez un fichier non vide avec les permissions appropriées sur votre système.
  • Utilisez le copy_file Boost Filesystem pour copier ce fichier sur la sortie souhaitée.

    void copy_file(const path& frompath, const path& topath) : Le contenu et les atsortingbuts du fichier référencé par frompath sont copiés dans le fichier référencé par topath. Cette routine attend qu’un fichier de destination soit absent; si le fichier de destination est présent, il génère une exception. Ceci, par conséquent, n’est pas équivalent à la commande cp spécifiée par le système sous UNIX. Il est également prévu que la variable frompath fasse référence à un fichier régulier approprié. Prenons l’exemple suivant: frompath fait référence à un lien symbolique / tmp / file1, qui à son tour fait référence à un fichier / tmp / file2; topath est, disons, / tmp / file3. Dans ce cas, copy_file échouera. C’est encore une autre différence que cette API a de l’air par rapport à la commande cp.

  • Maintenant, écrasez la sortie avec le contenu réel.

Mais, ce n’est qu’un hack auquel j’ai pensé longtemps après minuit. Prenez-le avec une pincée de sel et essayez ceci 🙂

Exemple multiplate-forme pour définir 0700 pour un fichier avec C ++ 17 et son std::filesystem .

 #include  //#include  #include  // Use this for most comstackrs as of yet. //namespace fs = std::filesystem; namespace fs = std::experimental::filesystem; // Use this for most comstackrs as of yet. int main() { fs::path myFile = "path/to/file.ext"; try { fs::permissions(myFile, fs::perms::owner_all); // Uses fs::perm_options::replace. } catch (std::exception& e) { // Handle exception or use another overload of fs::permissions() // with std::error_code. } } 

Voir std::filesystem::permissions , std::filesystem::perms et std::filesystem::perm_options .

Aucune idée si cela fonctionnerait, mais vous pourriez envisager d’utiliser l’exécutable chmod.exe fourni avec Cygwin.

Il n’y a pas de méthode standard pour faire cela en C ++, mais pour cette exigence spéciale, vous devriez probablement écrire un wrapper personnalisé, avec #ifdef _WIN32. Qt a un wrapper de permission dans sa classe QFile , mais cela signifierait bien sur dépendre de Qt …

Je viens de trouver quelques façons de faire facilement chmod 700 à partir de la ligne de commande Windows. Je vais poster une autre question demandant de l’aide pour trouver une structure de descripteur de sécurité win32 équivalente à utiliser (si je ne peux pas le comprendre dans les prochaines heures).

Windows 2000 et XP (désordonné – il semble toujours inciter):

 echo Y|cacls *onlyme.txt* /g %username%:F 

Windows 2003+:

 icacls *onlyme.txt* /inheritance:r /grant %username%:r 

MODIFIER:

Si vous pouviez utiliser l’ATL, cet article le couvre (Visual Studio n’est pas disponible): http://www.codeproject.com/KB/winsdk/accessctrl2.aspx

En fait, il indique que l’exemple de code inclut également un exemple de code non-ATL – il devrait avoir quelque chose qui fonctionne pour vous (et moi!)

La chose importante à retenir est d’obtenir r / w / x uniquement pour le propriétaire sur win32, il vous suffit de supprimer tous les descripteurs de sécurité du fichier et d’en append un avec un contrôle total.

Vous ne pouvez pas le faire de manière multi-plateforme. Sous Linux, vous devriez utiliser la fonction chmod(2) au lieu d’utiliser system(2) pour générer un nouveau shell. Sous Windows, vous devrez utiliser les différentes fonctions d’autorisation pour créer une liste de contrôle d’access avec les permissions appropriées.