View Javadoc
1   /**
2    * Waffle (https://github.com/dblock/waffle)
3    *
4    * Copyright (c) 2010 - 2015 Application Security, Inc.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Eclipse Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/epl-v10.html
10   *
11   * Contributors:
12   *     Application Security, Inc.
13   */
14  package waffle.util;
15  
16  import java.awt.Desktop;
17  import java.io.File;
18  import java.io.IOException;
19  import java.io.StringWriter;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.List;
23  
24  import javax.xml.parsers.DocumentBuilderFactory;
25  import javax.xml.parsers.ParserConfigurationException;
26  import javax.xml.transform.OutputKeys;
27  import javax.xml.transform.Transformer;
28  import javax.xml.transform.TransformerException;
29  import javax.xml.transform.TransformerFactory;
30  import javax.xml.transform.dom.DOMSource;
31  import javax.xml.transform.stream.StreamResult;
32  
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Element;
37  
38  import com.google.common.base.Charsets;
39  import com.google.common.io.Files;
40  import com.sun.jna.Platform;
41  import com.sun.jna.platform.WindowUtils;
42  import com.sun.jna.platform.win32.LMJoin;
43  import com.sun.jna.platform.win32.Netapi32Util;
44  import com.sun.jna.platform.win32.Win32Exception;
45  
46  import waffle.windows.auth.IWindowsAccount;
47  import waffle.windows.auth.IWindowsAuthProvider;
48  import waffle.windows.auth.IWindowsComputer;
49  import waffle.windows.auth.IWindowsDomain;
50  import waffle.windows.auth.impl.WindowsAccountImpl;
51  import waffle.windows.auth.impl.WindowsAuthProviderImpl;
52  
53  /**
54   * A Utility class to read system info to help troubleshoot WAFFLE system configuration.
55   * 
56   * <pre>
57   * This utility class collects system information and returns it as an XML document.
58   * </pre>
59   * 
60   * From the command line, you can write the info to stdout using:
61   * 
62   * <pre>
63   * <code>
64   *   java -cp "jna.jar;waffle-core.jar;waffle-api.jar;jna-platform.jar;guava-17.0.jar" waffle.util.WaffleInfo
65   * </code>
66   * </pre>
67   * 
68   * To show this information in a browser, run:
69   * 
70   * <pre>
71   * <code>
72   *   java -cp "..." waffle.util.WaffleInfo -show
73   * </code>
74   * </pre>
75   * 
76   * To lookup account names and return any listed info, run:
77   * 
78   * <pre>
79   * <code>
80   *   java -cp "..." waffle.util.WaffleInfo -lookup AccountName
81   * </code>
82   * </pre>
83   * 
84   */
85  public class WaffleInfo {
86  
87      /** The Constant LOGGER. */
88      private static final Logger LOGGER = LoggerFactory.getLogger(WaffleInfo.class);
89  
90      /**
91       * Get a Document with basic system information
92       * 
93       * This uses the builtin javax.xml package even though the API is quite verbose
94       * 
95       * @return Document with waffle info.
96       * 
97       * @throws ParserConfigurationException
98       *             when getting new document builder.
99       */
100     public Document getWaffleInfo() throws ParserConfigurationException {
101         final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
102 
103         // create the root element and add it to the document
104         final Element root = doc.createElement("waffle");
105 
106         // Add Version Information as attributes
107         String version = WaffleInfo.class.getPackage().getImplementationVersion();
108         if (version != null) {
109             root.setAttribute("version", version);
110         }
111         version = Platform.class.getPackage().getImplementationVersion();
112         if (version != null) {
113             root.setAttribute("jna", version);
114         }
115         version = WindowUtils.class.getPackage().getImplementationVersion();
116         if (version != null) {
117             root.setAttribute("jna-platform", version);
118         }
119 
120         doc.appendChild(root);
121         root.appendChild(this.getAuthProviderInfo(doc));
122 
123         return doc;
124     }
125 
126     /**
127      * Gets the auth provider info.
128      *
129      * @param doc
130      *            the doc
131      * @return the auth provider info
132      */
133     protected Element getAuthProviderInfo(final Document doc) {
134         final IWindowsAuthProvider auth = new WindowsAuthProviderImpl();
135 
136         final Element node = doc.createElement("auth");
137         node.setAttribute("class", auth.getClass().getName());
138 
139         // Current User
140         Element child = doc.createElement("currentUser");
141         node.appendChild(child);
142 
143         final String currentUsername = WindowsAccountImpl.getCurrentUsername();
144         this.addAccountInfo(doc, child, new WindowsAccountImpl(currentUsername));
145 
146         // Computer
147         child = doc.createElement("computer");
148         node.appendChild(child);
149 
150         final IWindowsComputer c = auth.getCurrentComputer();
151         Element value = doc.createElement("computerName");
152         value.setTextContent(c.getComputerName());
153         child.appendChild(value);
154 
155         value = doc.createElement("memberOf");
156         value.setTextContent(c.getMemberOf());
157         child.appendChild(value);
158 
159         value = doc.createElement("joinStatus");
160         value.setTextContent(c.getJoinStatus());
161         child.appendChild(value);
162 
163         value = doc.createElement("groups");
164         Element g;
165         for (final String s : c.getGroups()) {
166             g = doc.createElement("group");
167             g.setTextContent(s);
168             value.appendChild(g);
169         }
170         child.appendChild(value);
171 
172         // Only Show Domains if we are in a Domain
173         if (Netapi32Util.getJoinStatus() == LMJoin.NETSETUP_JOIN_STATUS.NetSetupDomainName) {
174             child = doc.createElement("domains");
175             node.appendChild(child);
176 
177             Element d;
178             for (final IWindowsDomain domain : auth.getDomains()) {
179                 d = doc.createElement("domain");
180                 node.appendChild(d);
181 
182                 value = doc.createElement("FQN");
183                 value.setTextContent(domain.getFqn());
184                 child.appendChild(value);
185 
186                 value = doc.createElement("TrustTypeString");
187                 value.setTextContent(domain.getTrustTypeString());
188                 child.appendChild(value);
189 
190                 value = doc.createElement("TrustDirectionString");
191                 value.setTextContent(domain.getTrustDirectionString());
192                 child.appendChild(value);
193             }
194         }
195         return node;
196     }
197 
198     /**
199      * Adds the account info.
200      *
201      * @param doc
202      *            the doc
203      * @param node
204      *            the node
205      * @param account
206      *            the account
207      */
208     protected void addAccountInfo(final Document doc, final Element node, final IWindowsAccount account) {
209         Element value = doc.createElement("Name");
210         value.setTextContent(account.getName());
211         node.appendChild(value);
212 
213         value = doc.createElement("FQN");
214         value.setTextContent(account.getFqn());
215         node.appendChild(value);
216 
217         value = doc.createElement("Domain");
218         value.setTextContent(account.getDomain());
219         node.appendChild(value);
220 
221         value = doc.createElement("SID");
222         value.setTextContent(account.getSidString());
223         node.appendChild(value);
224     }
225 
226     /**
227      * Gets the lookup info.
228      *
229      * @param doc
230      *            the doc
231      * @param lookup
232      *            the lookup
233      * @return the lookup info
234      */
235     public Element getLookupInfo(final Document doc, final String lookup) {
236         final IWindowsAuthProvider auth = new WindowsAuthProviderImpl();
237         final Element node = doc.createElement("lookup");
238         node.setAttribute("name", lookup);
239         try {
240             this.addAccountInfo(doc, node, auth.lookupAccount(lookup));
241         } catch (final Win32Exception ex) {
242             node.appendChild(WaffleInfo.getException(doc, ex));
243         }
244         return node;
245     }
246 
247     /**
248      * Gets the exception.
249      *
250      * @param doc
251      *            the doc
252      * @param t
253      *            the t
254      * @return the exception
255      */
256     public static Element getException(final Document doc, final Exception t) {
257         final Element node = doc.createElement("exception");
258         node.setAttribute("class", t.getClass().getName());
259 
260         Element value = doc.createElement("message");
261         if (t.getMessage() != null) {
262             value.setTextContent(t.getMessage());
263             node.appendChild(value);
264         }
265 
266         value = doc.createElement("trace");
267         value.setTextContent(Arrays.toString(t.getStackTrace()));
268         node.appendChild(value);
269         return node;
270     }
271 
272     /**
273      * To pretty xml.
274      *
275      * @param doc
276      *            the doc
277      * @return the string
278      * @throws TransformerException
279      *             the transformer exception
280      */
281     public static String toPrettyXML(final Document doc) throws TransformerException {
282         // set up a transformer
283         final TransformerFactory transfac = TransformerFactory.newInstance();
284         final Transformer trans = transfac.newTransformer();
285         trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
286         trans.setOutputProperty(OutputKeys.INDENT, "yes");
287 
288         // create string from xml tree
289         final StringWriter sw = new StringWriter();
290         final StreamResult result = new StreamResult(sw);
291         final DOMSource source = new DOMSource(doc);
292         trans.transform(source, result);
293         return sw.toString();
294     }
295 
296     /**
297      * Print system information.
298      * 
299      * @param args
300      *            variable arguments to pass to main. Valid values are "-show" and "-lookup".
301      */
302     public static void main(final String[] args) {
303         boolean show = false;
304         final List<String> lookup = new ArrayList<String>();
305         if (args != null) {
306             String arg;
307             for (int i = 0; i < args.length; i++) {
308                 arg = args[i];
309                 if ("-show".equals(arg)) {
310                     show = true;
311                 } else if ("-lookup".equals(arg)) {
312                     lookup.add(args[++i]);
313                 } else {
314                     WaffleInfo.LOGGER.error("Unknown Argument: {}", arg);
315                     throw new RuntimeException("Unknown Argument: " + arg);
316                 }
317             }
318         }
319 
320         final WaffleInfo helper = new WaffleInfo();
321         try {
322             final Document info = helper.getWaffleInfo();
323             for (final String name : lookup) {
324                 info.getDocumentElement().appendChild(helper.getLookupInfo(info, name));
325             }
326 
327             final String xml = WaffleInfo.toPrettyXML(info);
328             final File f;
329             if (show) {
330                 f = File.createTempFile("waffle-info-", ".xml");
331                 Files.write(xml, f, Charsets.UTF_8);
332                 Desktop.getDesktop().open(f);
333             } else {
334                 WaffleInfo.LOGGER.info(xml);
335             }
336         } catch (final IOException e) {
337             WaffleInfo.LOGGER.error(e.getMessage());
338             WaffleInfo.LOGGER.trace("{}", e);
339         } catch (final TransformerException e) {
340             WaffleInfo.LOGGER.error(e.getMessage());
341             WaffleInfo.LOGGER.trace("{}", e);
342         } catch (final ParserConfigurationException e) {
343             WaffleInfo.LOGGER.error(e.getMessage());
344             WaffleInfo.LOGGER.trace("{}", e);
345         }
346     }
347 }