2009-02-26 16 views
19

Sto usando C++ ofstream per scrivere un file. Voglio impostare le autorizzazioni affinché siano accessibili solo dall'utente: 700. In unix; Suppongo di poter emettere uno system("chmod 700 file.txt"); ma ho bisogno che questo codice funzioni anche su Windows. Posso usare alcune API di Windows; ma qual è il modo migliore in cui C++ può fare questo cross platform?C++ - Come impostare i permessi dei file (multipiattaforma)

+0

Orribile! Insicuro! Non creare il file, quindi correggere le sue autorizzazioni in seguito - e tanto meno usando un pazzo hack come 'system (" chmod ")'. Invece fallo nel modo giusto, creando autorizzazioni restrittive e usa un'API senza gara. Usa 'umask' (non thread-safe) o' open + fchmod + fdopen + std :: fstream'. –

+0

Non c'è niente di sbagliato nell'uso di chmod, purché chmod sia usato prima di scrivere i dati sensibili sul file. Ti rendi conto nella tua "risposta" che fchmod() è molto simile a chmod, giusto? – samoz

risposta

23

Ironia della sorte, mi sono imbattuto in questa stessa necessità prima di oggi.

Nel mio caso, la risposta è arrivata a quale livello di granularità dei permessi ho bisogno su Windows, contro Linux. Nel mio caso, mi interessa solo l'autorizzazione Utente, Gruppo e Altro su Linux. Su Windows, il permesso di lettura/scrittura di base di tutti gli avanzi da DOS è abbastanza buono per me, cioè non ho bisogno di trattare con ACL su Windows.

In generale, Windows dispone di due modelli privilegiati: il modello DOS di base e il modello di controllo di accesso più recente. Sotto il modello DOS c'è un tipo di privilegio: privilegio di scrittura. Tutti i file possono essere letti, quindi non c'è modo di disattivare il permesso di lettura (perché non esiste). Non esiste anche alcun concetto di permesso di esecuzione. Se un file può essere letto (la risposta è sì) ed è binario, allora può essere eseguito; altrimenti non può.

Il modello DOS di base è sufficiente per la maggior parte degli ambienti Windows, vale a dire ambienti in cui il sistema viene utilizzato da un singolo utente in un luogo fisico che può essere considerato relativamente sicuro. Il modello di controllo degli accessi è più complesso di diversi ordini di grandezza.

Il modello di controllo di accesso utilizza gli elenchi di controllo di accesso (ACL) per concedere i privilegi. I privilegi possono essere concessi solo da un processo con i privilegi necessari. Questo modello non solo consente il controllo di Utente, Gruppo e Altro con autorizzazione Lettura, Scrittura ed Esecuzione, ma consente anche il controllo dei file sulla rete e tra i domini Windows. (È possibile anche ottenere questo livello di follia su sistemi Unix con PAM.)

Nota: Il modello di controllo di accesso è disponibile solo su partizioni NTFS, se si utilizza partizioni FAT sei SOL.

Utilizzo ACL è un grande dolore nel culo. Non è un'impresa banale e richiede che tu impari non solo l'ACL, ma anche tutti i descrittori di sicurezza, i token di accesso e un sacco di altri concetti avanzati di sicurezza di Windows.

Fortunatamente per me, per le mie esigenze attuali, non ho bisogno la vera sicurezza che il modello di controllo di accesso fornisce. Riesco a passare fondamentalmente facendo finta di impostare le autorizzazioni su Windows, a patto che imposti realmente le autorizzazioni su Linux.

Windows supporta quello che chiamano un versione "ISO C++ conforme" di chmod (2). Questa API è chiamata _chmod ed è simile a chmod (2), ma più limitata e non di tipo o nome compatibile (ovviamente). Windows ha anche un chmod deprecato, quindi non puoi semplicemente aggiungere chmod a Windows e usare il chmod dritto (2) su Linux.

ho scritto il seguente:

#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 

E 'importante ricordare che la mia soluzione prevede solo DOS di sicurezza tipo. Questo è anche noto come non sicurezza, ma è la quantità di sicurezza che la maggior parte delle app ti dà su Windows.

Inoltre, sotto la mia soluzione, se non definisci STRICT_UGO_PERMISSIONS, quando dai il permesso al gruppo o ad altro (o lo rimuovi per questo), stai davvero cambiando il proprietario. Se non vuoi farlo, ma non hai ancora bisogno di autorizzazioni ACL complete per Windows, basta definire STRICT_UGO_PERMISSIONS.

8

Non esiste un modo multipiattaforma per eseguire questa operazione. Windows non supporta i permessi dei file in stile Unix. Per fare ciò che vuoi, dovrai cercare di creare un elenco di controllo di accesso per quel file, che ti consentirà di definire esplicitamente le autorizzazioni di accesso per utenti e gruppi.

Un'alternativa potrebbe essere quella di creare il file in una directory le cui impostazioni di sicurezza sono già state impostate per escludere tutti tranne l'utente.

+0

Questa risposta è qualcosa a cui le persone dovrebbero davvero pensare: il trattamento delle autorizzazioni nelle app è davvero necessario? In molti, molti casi non lo è e addirittura rompe le cose, perché consente ad amministratori e utenti di controllare le autorizzazioni come meglio credono. Soprattutto per es. su Windows, nessun altro utente è in grado di accedere alle directory home dell'utente in ogni caso, quindi probabilmente non è necessario rendere i file "privati" manualmente. Un sacco di chmod + umask-handling nelle applicazioni Linux è legacy a causa della mancanza di ACL, ereditarietà adeguata e così via. Cerca di evitarlo il più possibile. –

1

Non ho idea se avrebbe funzionato, ma è possibile esaminare utilizzando l'eseguibile chmod.exe fornito con Cygwin.

+0

+1 perché è un'idea interessante. So che chmod under cygwin fa The Right Thing ogni volta che l'ho provato. – rmeador

+0

Cosa? Cambia ACL NTFS? – aib

1

Non c'è un modo standard per farlo in C++, ma per questo requisito speciale probabilmente dovresti scrivere solo un wrapper personalizzato, con #ifdef _WIN32. Qt ha un wrapper di autorizzazione nella sua classe QFile, ma ciò ovviamente significherebbe in base a Qt ...

+0

L'avviso sul collegamento fornito implica che questo potrebbe non funzionare come previsto su Windows. – Ferruccio

0

Non è possibile farlo in modo multipiattaforma. In Linux, dovresti usare la funzione chmod(2) invece di usare system(2) per generare una nuova shell. Su Windows, dovrai usare i vari authorization functions per creare un ACL (access-control list) con le autorizzazioni appropriate.

3

La chiamata system() è una strana bestia. Sono stato morso da un'implementazione di un sistema NOP() su un Mac molte lune fa. La sua implementazione è definita dal fatto che lo standard non definisce cosa un'implementazione (piattaforma/compilatore) dovrebbe fare. Sfortunatamente, questo è anche l'unico modo standard per fare qualcosa al di fuori della portata della tua funzione (nel tuo caso - cambiare i permessi).

Aggiornamento: A proposto mod:

  • Creare un file non vuoto con le autorizzazioni appropriate sul vostro sistema.
  • Utilizzare Boost Filesystem copy_file per copiare questo file nell'output desiderato.

    void copy_file(const path& frompath, const path& topath): I contenuti e attributi del file a cui si riferisce frompath vengono copiati il ​​file a cui si riferisce topath. Questa routine si aspetta che un file di destinazione sia assente; se il file di destinazione è presente, genera un'eccezione. Questo, quindi, non è equivalente al comando cp specificato dal sistema in UNIX. Si prevede inoltre che la variabile frompath faccia riferimento a un file regolare appropriato. Considera questo esempio: frompath fa riferimento a un link simbolico/tmp/file1, che a sua volta si riferisce a un file/tmp/file2; topath è, per esempio,/tmp/file3. In questa situazione, copy_file fallirà. Questa è un'altra differenza rispetto a questa API rispetto al comando cp.

  • Ora, sovrascrivere l'output con i contenuti effettivi.

Ma, questo è solo un hack ho pensato a lungo dopo la mezzanotte. Prendilo con un pizzico di sale e provalo :)

1

Ho appena trovato un paio di modi per fare chmod 700 facilmente dalla riga di comando di Windows. Inserirò un'altra domanda per chiedere aiuto su una struttura di descrittore di sicurezza win32 equivalente da usare (se non riesco a capirlo nelle prossime ore).

Windows 2000 & XP (messy- sembra sempre per richiedere):

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

di Windows 2003+:

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

EDIT:

Se avete avuto la possibilità di utilizzare l'ATL, questo articolo lo copre (non sono disponibili Visual Studio): http://www.codeproject.com/KB/winsdk/accessctrl2.aspx

In realtà, dice il codice di esempio include la non-ATL codice di esempio, come ben si dovrebbe avere qualcosa che funziona per voi (e me!)

La cosa importante da ricordare è quello di ottenere r/w/x per il proprietario solo su win32, devi solo cancellare tutti i descrittori di sicurezza dal file e aggiungerne uno per te con il pieno controllo.

2

Esempio multipiattaforma per impostare 0700 per un file con C++ 17 e il suo 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. 
    }   
} 

Vedi std::filesystem::permissions, std::filesystem::perms e std::filesystem::perm_options.

Problemi correlati