copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / tools / mono-configuration-crypto / cli / main.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Reflection;
5
6 using Mono.Options;
7 using Mono.Configuration.Crypto;
8
9 [assembly: AssemblyTitle ("mono-configuration-crypto")]
10 [assembly: AssemblyDescription ("Mono configuration utility to manage encryption keys and encrypt/decrypt config file sections")]
11 [assembly: AssemblyCompany (Consts.MonoCompany)]
12 [assembly: AssemblyProduct ("Mono Configuration Cryptography Tools")]
13 [assembly: AssemblyCopyright ("Copyright (c) 2010 Novell, Inc (http://novell.com, http://mono-project.com/)")]
14 [assembly: AssemblyVersion (Consts.FxVersion)]
15
16 namespace MonoConfigurationCrypto
17 {
18         class MonoConfigurationCrypto
19         {
20                 void Success ()
21                 {
22                         Console.WriteLine ("Success.");
23                 }
24                 
25                 void Failure (Exception ex, Config cfg)
26                 {
27                         Failure (ex, cfg, null);
28                 }
29
30                 void Failure (Config cfg, string message, params object[] parms)
31                 {
32                         Failure (null, cfg, message, parms);
33                 }
34                 
35                 void Failure (Exception ex, Config cfg, string message, params object[] parms)
36                 {
37                         if (!String.IsNullOrEmpty (message)) {
38                                 if (parms == null || parms.Length == 0)
39                                         Console.Error.WriteLine (message);
40                                 else
41                                         Console.Error.WriteLine (message, parms);
42                         }
43                         
44                         if (ex != null) {
45                                 if (cfg.Verbose)
46                                         Console.Error.WriteLine (ex.ToString ());
47                                 else
48                                         Console.Error.WriteLine (ex.Message);
49                         }
50                         Console.Error.WriteLine ("Failure.");
51                 }
52                 
53                 bool ListContainers (Config cfg)
54                 {
55                         try {
56                                 var kc = new KeyContainerCollection (cfg.UseMachinePath);
57                                 int count;
58                                 foreach (KeyContainer c in kc) {
59                                         count = c.Count;
60                                         Console.WriteLine ("{0} container '{1}' ({2} key{3})", c.Local ? "Local" : "Global", c.Name, count,
61                                                            count == 1 ? String.Empty : "s");
62                                 }
63                         } catch (Exception ex) {
64                                 Failure (ex, cfg);
65                         }
66                         
67                         return true;
68                 }
69
70                 string FindConfigFile (string path, string configFileName)
71                 {
72                         if (!Directory.Exists (path))
73                                 return null;
74                         
75                         string fileName = null;
76                         foreach (var s in Directory.GetFiles (path, "*.*", SearchOption.TopDirectoryOnly)) {
77                                 string fn = Path.GetFileName (s);
78                                 if (String.Compare (fn, configFileName, StringComparison.OrdinalIgnoreCase) == 0) {
79                                         fileName = fn;
80                                         break;
81                                 }
82                         }
83
84                         if (fileName == null)
85                                 return null;
86
87                         return Path.Combine (path, fileName);
88                 }
89                 
90                 bool EncryptSection (Config cfg)
91                 {
92                         string configSection = cfg.ConfigSectionName;
93                         string containerName = cfg.ContainerName;
94                         string configFile = FindConfigFile (cfg.ApplicationPhysicalPath, cfg.ConfigFileName);
95
96                         Console.WriteLine ("Encrypting section '{0}' in config file '{1}' using key container '{2}'...", configSection, configFile, containerName);
97                         
98                         if (String.IsNullOrEmpty (configFile)) {
99                                 Failure (cfg, "No config file found in directory '{0}'", cfg.ApplicationPhysicalPath);
100                                 return true;
101                         }
102                         
103                         if (String.IsNullOrEmpty (configSection)) {
104                                 Failure (cfg, "No config section name specified.");
105                                 return true;
106                         }
107                         
108                         try {
109                                 var cs = new ConfigSection ();
110                                 cs.Encrypt (configFile, configSection, containerName, cfg.UseMachinePath);
111                                 Console.WriteLine ("Success.");
112                         } catch (Exception ex) {
113                                 Failure (ex, cfg);
114                                 return true;
115                         }
116                         
117                         return false;
118                 }
119
120                 bool DecryptSection (Config cfg)
121                 {
122                         string configSection = cfg.ConfigSectionName;
123                         string containerName = cfg.ContainerName;
124                         string configFile = FindConfigFile (cfg.ApplicationPhysicalPath, cfg.ConfigFileName);
125
126                         Console.WriteLine ("Decrypting section '{0}' in config file '{1}' using key container '{2}'...", configSection, configFile, containerName);
127                         
128                         if (String.IsNullOrEmpty (configFile)) {
129                                 Failure (cfg, "No config file found in directory '{0}'", cfg.ApplicationPhysicalPath);
130                                 return true;
131                         }
132                         
133                         if (String.IsNullOrEmpty (configSection)) {
134                                 Failure (cfg, "No config section name specified.");
135                                 return true;
136                         }
137                         
138                         try {
139                                 var cs = new ConfigSection ();
140                                 cs.Decrypt (configFile, configSection, containerName, cfg.UseMachinePath);
141                                 Console.WriteLine ("Success.");
142                         } catch (Exception ex) {
143                                 Failure (ex, cfg);
144                                 return true;
145                         }
146                         
147                         return false;
148                 }
149
150                 bool CreateKey (Config cfg)
151                 {
152                         string name = cfg.ContainerName;
153                         KeyContainerCollection kc;
154
155                         Console.WriteLine ("Creating RSA key container '{0}'...", name);
156                         try {
157                                 kc = new KeyContainerCollection (cfg.UseMachinePath);
158                                 if (kc.Contains (name)) {
159                                         Failure (cfg, "The RSA container already exists.");
160                                         return true;
161                                 }
162
163                                 var k = new Key (name, cfg.KeySize, cfg.UseMachinePath);
164                                 if (!k.IsValid) {
165                                         Failure (cfg, "Failed to generate RSA key pair.");
166                                         return true;
167                                 }
168                                 k.Save ();
169                                 Success ();
170                         } catch (Exception ex) {
171                                 Failure (ex, cfg);
172                                 return true;
173                         }
174                         
175                         return false;
176                 }
177
178                 bool ImportKey (Config cfg)
179                 {
180                         string containerName = cfg.ContainerName;
181                         string fileName = cfg.FileName;
182
183                         Console.WriteLine ("Importing an RSA key from file '{0}' into the container '{1}'...", fileName, containerName);
184                         if (String.IsNullOrEmpty (containerName)) {
185                                 Failure (cfg, "Unspecified container name.");
186                                 return true;
187                         }
188
189                         if (String.IsNullOrEmpty (fileName)) {
190                                 Failure (cfg, "Unspecified file name.");
191                                 return true;
192                         }
193
194                         if (!File.Exists (fileName)) {
195                                 Failure (cfg, "Key file '{0}' does not exist.", fileName);
196                                 return true;
197                         }
198
199                         KeyContainerCollection kcc;
200                         Key key;
201                         KeyContainer kc;
202                         try {
203                                 kcc = new KeyContainerCollection (cfg.UseMachinePath);
204                                 kc = kcc [containerName];
205                                 
206                                 if (kc != null)
207                                         key = kc [0];
208                                 else
209                                         key = null;
210
211                                 // No validation is performed on the key - this is left for the
212                                 // encryption algorithm implementation to do.
213                                 string keyvalue = File.ReadAllText (fileName);
214                                 if (key == null)
215                                         key = new Key (containerName, keyvalue, cfg.UseMachinePath);
216                                 else {
217                                         key.KeyValue = keyvalue;
218                                         key.ContainerName = containerName;
219                                 }
220
221                                 key.Save ();
222                                 Console.WriteLine ("Success.");
223                         } catch (Exception ex) {
224                                 Failure (ex, cfg);
225                                 return true;
226                         }
227                         
228                         return false;
229                 }
230
231                 bool ExportKey (Config cfg)
232                 {
233                         string containerName = cfg.ContainerName;
234                         string fileName = cfg.FileName;
235
236                         Console.WriteLine ("Exporting an RSA key from container '{0}' to file '{1}'...", containerName, fileName);
237                         if (String.IsNullOrEmpty (containerName)) {
238                                 Failure (cfg, "Unspecified container name.");
239                                 return true;
240                         }
241
242                         if (String.IsNullOrEmpty (fileName)) {
243                                 Failure (cfg, "Unspecified file name.");
244                                 return true;
245                         }
246
247                         KeyContainerCollection kcc;
248                         Key key;
249                         KeyContainer kc;
250                         try {
251                                 kcc = new KeyContainerCollection (cfg.UseMachinePath);
252                                 kc = kcc [containerName];
253                                 
254                                 if (kc != null)
255                                         key = kc [0];
256                                 else {
257                                         Failure (cfg, "Container '{0}' does not exist.", containerName);
258                                         return true;
259                                 }
260
261                                 if (key == null) {
262                                         Failure (cfg, "Container '{0}' exists but it does not contain any keys.", containerName);
263                                         return true;
264                                 }
265                                 
266                                 File.WriteAllText (fileName, key.KeyValue);
267                                 Console.WriteLine ("Success.");
268                         } catch (Exception ex) {
269                                 Failure (ex, cfg);
270                                 return true;
271                         }
272                         
273                         return false;
274                 }
275
276                 bool RemoveContainer (Config cfg)
277                 {
278                         string containerName = cfg.ContainerName;
279                         Console.WriteLine ("Removing container '{0}'...", containerName);
280                         if (String.IsNullOrEmpty (containerName)) {
281                                 Failure (cfg, "Unspecified container name.");
282                                 return true;
283                         }
284
285                         try {
286                                 KeyContainer.RemoveFromDisk (containerName, cfg.UseMachinePath);
287                                 
288                                 Console.WriteLine ("Success.");
289                         } catch (Exception ex) {
290                                 Failure (ex, cfg);
291                                 return true;
292                         }
293                         
294                         return false;
295                 }
296                 
297                 void ShowHeader ()
298                 {
299                         string title = String.Empty, version = String.Empty, description = String.Empty, copyright = String.Empty;
300                         Assembly asm = Assembly.GetExecutingAssembly ();
301                         foreach (object o in asm.GetCustomAttributes (false)) {
302                                 if (o is AssemblyTitleAttribute)
303                                         title = ((AssemblyTitleAttribute)o).Title;
304                                 else if (o is AssemblyCopyrightAttribute)
305                                         copyright = ((AssemblyCopyrightAttribute)o).Copyright;
306                                 else if (o is AssemblyDescriptionAttribute)
307                                         description = ((AssemblyDescriptionAttribute)o).Description;
308                         }
309
310                         version = asm.GetName ().Version.ToString ();
311                         
312                         Console.WriteLine ("{1} - version {2}{0}{3}{0}{4}{0}",
313                                            Environment.NewLine,
314                                            title,
315                                            version,
316                                            description,
317                                            copyright);
318                 }
319                 
320                 void Run (string[] args)
321                 {
322                         var cfg = new Config ();
323                         var actions = new List <Func <Config, bool>> ();
324                         var options = new OptionSet () {
325                                 { "h|?|help", "Show usage information", v => cfg.ShowHelp = true },
326                                 { "v|verbose", "Show verbose information (including exception stacktraces)", v => cfg.Verbose = true },
327                                 { "m|machine|global", "Use machine (global) store for all the key actions", v => cfg.UseMachinePath = true },
328                                 { "u|user|local", "Use local (user) store for all the key actions [*]", v => cfg.UseMachinePath = false },
329                                 { "l|list", "List all the key container names in the store", v => actions.Add (ListContainers) },
330                                 { "c|create", "Creates an RSA public/private key pair", v => actions.Add (CreateKey) },
331                                 { "i|import", "Import key to a container", v => actions.Add (ImportKey) },
332                                 { "x|export", "Export key from a container", v => actions.Add (ExportKey) },
333                                 { "r|remove", "Remove a container", v => actions.Add (RemoveContainer) },
334                                 { "f=|file=", "File name for import or export operations", (string s) => cfg.FileName = s },
335                                 { "cf=|config-file=", String.Format ("Config file name (not path) [{0}]", Config.DefaultConfigFileName), (string s) => cfg.ConfigFileName = s },
336                                 { "n=|name=", String.Format ("Container name [{0}]", Config.DefaultContainerName), (string s) => cfg.ContainerName = s },
337                                 { "s=|size=", String.Format ("Key size [{0}]", Config.DefaultKeySize), (uint s) => cfg.KeySize = s },
338                                 { "p=|path=", String.Format ("Application physical path [{0}]", Config.DefaultApplicationPhysicalPath), (string s) => cfg.ApplicationPhysicalPath = s },
339                                 { "d=|dec=|decrypt=", "Decrypt configuration section", (string s) => { cfg.ConfigSectionName = s; actions.Add (DecryptSection);} },
340                                 { "e=|enc=|encrypt=", "Encrypt configuration section", (string s) => { cfg.ConfigSectionName = s; actions.Add (EncryptSection);} },
341                         };
342                         options.Parse (args);
343
344                         if (cfg.ShowHelp) {
345                                 ShowHeader ();
346                                 options.WriteOptionDescriptions (Console.Out);
347                                 return;
348                         }
349                         
350                         foreach (var action in actions)
351                                 if (action (cfg))
352                                         Environment.Exit (0);
353                 }
354                 
355                 static void Main (string[] args)
356                 {
357                         new MonoConfigurationCrypto ().Run (args);
358                 }
359         }
360 }