2009-02-26 25 views
19

Estoy usando C++ ofstream para escribir un archivo. Quiero configurar los permisos para que solo el usuario pueda acceder a ellos: 700. En Unix; Supongo que puedo simplemente emitir un system("chmod 700 file.txt");, pero también necesito que este código funcione en Windows. Puedo usar algunas API de Windows; pero, ¿cuál es la mejor forma de plataforma cruzada de C++ para hacer esto?C++ - Cómo configurar permisos de archivos (plataforma cruzada)

+0

¡Horrible! ¡Inseguro! No cree el archivo y luego corrija sus permisos, y mucho menos con un hack loco como 'system (" chmod ")'. En cambio, hágalo de la manera correcta, cree con permisos restrictivos y use una API libre de raza. Use 'umask' (no seguro para subprocesos) o' open + fchmod + fdopen + std :: fstream'. –

+0

No hay nada malo con el uso de chmod, siempre que se use chmod antes de escribir los datos confidenciales en el archivo. En su "respuesta" se da cuenta de que fchmod() es muy similar a chmod, ¿verdad? – samoz

Respuesta

23

Irónicamente, acabo de encontrarme con esta misma necesidad el día de hoy.

En mi caso, la respuesta vino al nivel de granularidad de permisos que necesito en Windows, en comparación con Linux. En mi caso, solo me importan los permisos de Usuario, Grupo y Otro en Linux. En Windows, el permiso básico de Read/Write All del DOS es lo suficientemente bueno para mí, es decir, no necesito lidiar con ACL en Windows.

En general, Windows tiene dos modelos de privilegios: el modelo básico de DOS y el modelo de control de acceso más nuevo. Bajo el modelo de DOS hay un tipo de privilegio: privilegio de escritura. Todos los archivos se pueden leer, por lo que no hay forma de desactivar el permiso de lectura (porque no existe). Tampoco hay ningún concepto de permiso de ejecución. Si un archivo se puede leer (la respuesta es sí) y es binario, entonces se puede ejecutar; de lo contrario no puede.

El modelo básico de DOS es suficiente para la mayoría de los entornos de Windows, es decir, entornos donde el sistema es utilizado por un solo usuario en una ubicación física que puede considerarse relativamente segura. El modelo de control de acceso es más complejo en varios órdenes de magnitud.

El modelo de control de acceso utiliza listas de control de acceso (ACL) para otorgar privilegios. Los privilegios solo pueden ser otorgados por un proceso con los privilegios necesarios. Este modelo no solo permite el control de usuarios, grupos y otros con permisos de lectura, escritura y ejecución, sino que también permite el control de archivos a través de la red y entre dominios de Windows. (También puede obtener este nivel de locura en los sistemas Unix con PAM.)

Nota: El modelo de Control de acceso solo está disponible en particiones NTFS, si está utilizando particiones FAT usted es SOL.

Usar ACL es un gran dolor en el culo. No es una tarea trivial y requerirá que aprendas no solo ACL sino también todo sobre descriptores de seguridad, tokens de acceso y muchos otros conceptos avanzados de seguridad de Windows.

Afortunadamente para mí, para mis necesidades actuales, no necesito la verdadera seguridad que brinda el modelo de control de acceso. Puedo salir adelante con básicamente pretender establecer permisos en Windows, siempre y cuando realmente establezca permisos en Linux.

Windows admite lo que ellos llaman una versión "conforme a ISO C++" de chmod (2). Esta API se llama _chmod, y es similar a chmod (2), pero más limitada y no es compatible con el tipo o el nombre (por supuesto). Windows también tiene un chmod desaprobado, por lo que no puede simplemente agregar chmod a Windows y usar el chmod directo (2) en Linux.

me escribió lo siguiente:

#include <sys/stat.h> 
#include <sys/types.h> 

#ifdef _WIN32 
# include <io.h> 

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 

Es importante recordar que mi solución sólo proporciona la seguridad de tipo DOS. Esto también se conoce como falta de seguridad, pero es la cantidad de seguridad que la mayoría de las aplicaciones le brindan en Windows.

Además, bajo mi solución, si no define STRICT_UGO_PERMISSIONS, cuando da un permiso para agrupar u otro (o lo elimina para el caso), realmente está cambiando el propietario. Si no deseaba hacer eso, pero aún no necesitaba permisos completos de Windows ACL, simplemente defina STRICT_UGO_PERMISSIONS.

8

No hay una forma de plataforma cruzada para hacer esto. Windows no admite permisos de archivos de estilo Unix. Para hacer lo que desea, tendrá que considerar la creación de una lista de control de acceso para ese archivo, que le permitirá definir explícitamente los permisos de acceso para usuarios y grupos.

Una alternativa podría ser crear el archivo en un directorio cuya configuración de seguridad ya se haya configurado para excluir a todos menos al usuario.

+0

Esa respuesta es algo en lo que la gente realmente debería pensar: ¿Es realmente necesario lidiar con los permisos en las aplicaciones? En muchos, muchos casos no lo es, e incluso rompe las cosas, porque les lleva a los administradores y usuarios la posibilidad de controlar los permisos como mejor les parezca. Especialmente p. en Windows, ningún otro usuario puede acceder a los directorios principales del usuario de manera predeterminada, por lo que es muy probable que no sea necesario hacer que los archivos sean "privados" de forma manual. Mucha de la manipulación de chmod + umask en aplicaciones de Linux es heredada debido a la falta de ACL, herencia propia y demás. Intenta evitar eso tanto como sea posible. –

1

No hay idea de si funcionaría, pero podría examinar el uso del ejecutable chmod.exe que viene con Cygwin.

+0

+1 porque es una idea interesante. Sé que chmod en cygwin hace Lo correcto cada vez que lo intento. – rmeador

+0

¿Qué? ¿Cambia las ACL de NTFS? – aib

1

No hay una forma estándar de hacerlo en C++, pero para este requisito especial, probablemente solo deba escribir un contenedor personalizado, con #ifdef _WIN32. Qt tiene un contenedor de permisos en su clase QFile, pero esto por supuesto significa que depende de Qt ...

+0

La advertencia en el enlace que proporcionó implica que esto podría no funcionar como se esperaba en Windows. – Ferruccio

0

No puede hacerlo de manera multiplataforma. En Linux, debe usar la función chmod(2) en lugar de usar system(2) para generar un nuevo shell. En Windows, tendrá que usar los diversos authorization functions para hacer una ACL (lista de control de acceso) con los permisos adecuados.

3

La llamada system() es una bestia extraña. Me ha picado una implementación de sistema NOP() en una Mac hace muchas lunas. Su implementación está definida, lo que significa que el estándar no define qué debe hacer una implementación (plataforma/compilador). Lamentablemente, esta es también la única forma estándar de hacer algo fuera del alcance de su función (en su caso, cambiar los permisos).

Actualización: Un truco propuso:

  • Crear un archivo que no esté vacía con los permisos apropiados en el sistema.
  • Utilice Boost Filesystem copy_file para copiar este archivo a su salida deseada.

    void copy_file(const path& frompath, const path& topath): Los contenidos y atributos del archivo al que se hace referencia frompath se copian en el archivo al que se refiere bypath. Esta rutina espera que un archivo de destino esté ausente; si el archivo de destino está presente, arroja una excepción. Esto, por lo tanto, no es equivalente al comando cp especificado por el sistema en UNIX. También se espera que la variable frompath se refiera a un archivo regular apropiado. Considere este ejemplo: frompath se refiere a un enlace simbólico/tmp/file1, que a su vez se refiere a un archivo/tmp/file2; topath es, por ejemplo,/tmp/file3. En esta situación, copy_file fallará. Esta es otra diferencia más que esta API se comporta en comparación con el comando cp.

  • Ahora, sobrescriba la salida con los contenidos reales.

Pero, esto es solo un truco que pensé mucho después de la medianoche. Tómelo con un poquito de sal y pruebe esto :)

1

Acabo de encontrar un par de maneras de hacer chmod 700 fácilmente desde la línea de comandos de Windows. Voy a publicar otra pregunta pidiendo ayuda para crear una estructura de descriptor de seguridad win32 equivalente (si no puedo resolverlo en las próximas horas).

Windows 2000 XP & (messy- parece que siempre pedirá):

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

de Windows 2003+:

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

EDIT:

Si tuviera la capacidad de utilice el ATL, este artículo lo cubre (no tengo Visual Studio disponible): http://www.codeproject.com/KB/winsdk/accessctrl2.aspx

De hecho, se dice que el código de ejemplo incluye código de ejemplo no ATL como bien se debe tener algo que funcione para usted (y yo)

Lo importante para recordar es conseguir r/w/x para el propietario solo en win32, solo tiene que borrar todas las descripciones de seguridad del archivo y agregar una para usted con control total.

2

Ejemplo de plataforma cruzada para configurar 0700 para un archivo con C++ 17 y su std::filesystem.

#include <exception> 
//#include <filesystem> 
#include <experimental/filesystem> // Use this for most compilers as of yet. 

//namespace fs = std::filesystem; 
namespace fs = std::experimental::filesystem; // Use this for most compilers 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. 
    }   
} 

Ver std::filesystem::permissions, std::filesystem::perms y std::filesystem::perm_options.

Cuestiones relacionadas