about summary refs log tree commit diff
path: root/libnm-util
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-util')
-rw-r--r--libnm-util/COPYING488
-rw-r--r--libnm-util/Makefile.am113
-rw-r--r--libnm-util/Makefile.in1121
-rw-r--r--libnm-util/crypto.c636
-rw-r--r--libnm-util/crypto.h131
-rw-r--r--libnm-util/crypto_gnutls.c448
-rw-r--r--libnm-util/crypto_nss.c562
-rw-r--r--libnm-util/libnm-util.pc.in13
-rw-r--r--libnm-util/libnm-util.ver394
-rw-r--r--libnm-util/nm-connection.c1210
-rw-r--r--libnm-util/nm-connection.h167
-rw-r--r--libnm-util/nm-param-spec-specialized.c973
-rw-r--r--libnm-util/nm-param-spec-specialized.h46
-rw-r--r--libnm-util/nm-setting-8021x.c3440
-rw-r--r--libnm-util/nm-setting-8021x.h280
-rw-r--r--libnm-util/nm-setting-bluetooth.c282
-rw-r--r--libnm-util/nm-setting-bluetooth.h85
-rw-r--r--libnm-util/nm-setting-cdma.c363
-rw-r--r--libnm-util/nm-setting-cdma.h83
-rw-r--r--libnm-util/nm-setting-connection.c521
-rw-r--r--libnm-util/nm-setting-connection.h110
-rw-r--r--libnm-util/nm-setting-gsm.c656
-rw-r--r--libnm-util/nm-setting-gsm.h135
-rw-r--r--libnm-util/nm-setting-ip4-config.c1287
-rw-r--r--libnm-util/nm-setting-ip4-config.h177
-rw-r--r--libnm-util/nm-setting-ip6-config.c1120
-rw-r--r--libnm-util/nm-setting-ip6-config.h170
-rw-r--r--libnm-util/nm-setting-olpc-mesh.c296
-rw-r--r--libnm-util/nm-setting-olpc-mesh.h84
-rw-r--r--libnm-util/nm-setting-ppp.c730
-rw-r--r--libnm-util/nm-setting-ppp.h113
-rw-r--r--libnm-util/nm-setting-pppoe.c274
-rw-r--r--libnm-util/nm-setting-pppoe.h83
-rw-r--r--libnm-util/nm-setting-serial.c314
-rw-r--r--libnm-util/nm-setting-serial.h87
-rw-r--r--libnm-util/nm-setting-vpn.c464
-rw-r--r--libnm-util/nm-setting-vpn.h106
-rw-r--r--libnm-util/nm-setting-wired.c384
-rw-r--r--libnm-util/nm-setting-wired.h88
-rw-r--r--libnm-util/nm-setting-wireless-security.c1208
-rw-r--r--libnm-util/nm-setting-wireless-security.h133
-rw-r--r--libnm-util/nm-setting-wireless.c909
-rw-r--r--libnm-util/nm-setting-wireless.h115
-rw-r--r--libnm-util/nm-setting.c752
-rw-r--r--libnm-util/nm-setting.h179
-rw-r--r--libnm-util/nm-utils-private.h37
-rw-r--r--libnm-util/nm-utils.c2104
-rw-r--r--libnm-util/nm-utils.h218
-rw-r--r--libnm-util/tests/Makefile.am139
-rw-r--r--libnm-util/tests/Makefile.in935
-rw-r--r--libnm-util/tests/certs/Makefile.am20
-rw-r--r--libnm-util/tests/certs/Makefile.in437
-rw-r--r--libnm-util/tests/certs/ca-no-ending-newline.pem15
-rw-r--r--libnm-util/tests/certs/test-cert.p12bin0 -> 4092 bytes
-rw-r--r--libnm-util/tests/certs/test-key-only-decrypted.derbin0 -> 1192 bytes
-rw-r--r--libnm-util/tests/certs/test-key-only.pem92
-rw-r--r--libnm-util/tests/certs/test2-cert.p12bin0 -> 4136 bytes
-rw-r--r--libnm-util/tests/certs/test2_ca_cert.pem27
-rw-r--r--libnm-util/tests/certs/test2_key_and_cert.pem119
-rw-r--r--libnm-util/tests/certs/test_ca_cert.pem27
-rw-r--r--libnm-util/tests/certs/test_key_and_cert.pem119
-rw-r--r--libnm-util/tests/test-crypto.c317
-rw-r--r--libnm-util/tests/test-general.c246
-rw-r--r--libnm-util/tests/test-need-secrets.c566
-rw-r--r--libnm-util/tests/test-setting-8021x.c163
-rw-r--r--libnm-util/tests/test-settings-defaults.c135
66 files changed, 27046 insertions, 0 deletions
diff --git a/libnm-util/COPYING b/libnm-util/COPYING
new file mode 100644
index 00000000..2ec1380e
--- /dev/null
+++ b/libnm-util/COPYING
@@ -0,0 +1,488 @@
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+		51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the 
+    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA  02110-1301  USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am
new file mode 100644
index 00000000..ddc68408
--- /dev/null
+++ b/libnm-util/Makefile.am
@@ -0,0 +1,113 @@
+SUBDIRS=. tests
+
+INCLUDES = -I${top_srcdir} -I${top_srcdir}/include
+
+lib_LTLIBRARIES=libnm-util.la
+
+libnm_util_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(UUID_CFLAGS) \
+	-DG_DISABLE_DEPRECATED
+
+libnm_util_include_HEADERS = 		\
+	nm-connection.h			\
+	nm-setting.h			\
+	nm-setting-8021x.h		\
+	nm-setting-bluetooth.h		\
+	nm-setting-connection.h		\
+	nm-setting-ip4-config.h		\
+	nm-setting-ip6-config.h		\
+	nm-setting-ppp.h		\
+	nm-setting-pppoe.h		\
+	nm-setting-serial.h		\
+	nm-setting-gsm.h		\
+	nm-setting-cdma.h		\
+	nm-setting-olpc-mesh.h		\
+	nm-setting-wired.h		\
+	nm-setting-wireless.h		\
+	nm-setting-wireless-security.h	\
+	nm-setting-vpn.h		\
+	nm-utils.h
+
+libnm_util_la_SOURCES=			\
+	crypto.c			\
+	crypto.h			\
+	nm-connection.c			\
+	nm-param-spec-specialized.c	\
+	nm-param-spec-specialized.h \
+	nm-setting.c			\
+	nm-setting-8021x.c		\
+	nm-setting-bluetooth.c		\
+	nm-setting-connection.c		\
+	nm-setting-ip4-config.c		\
+	nm-setting-ip6-config.c		\
+	nm-setting-ppp.c		\
+	nm-setting-pppoe.c		\
+	nm-setting-serial.c		\
+	nm-setting-gsm.c		\
+	nm-setting-cdma.c		\
+	nm-setting-olpc-mesh.c		\
+	nm-setting-wired.c		\
+	nm-setting-wireless.c		\
+	nm-setting-wireless-security.c	\
+	nm-setting-vpn.c		\
+	nm-utils.c			\
+	nm-utils-private.h \
+	$(libnm_util_include_HEADERS)
+
+libnm_util_la_LIBADD = $(GLIB_LIBS) $(DBUS_LIBS) $(UUID_LIBS)
+
+libnm_util_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnm-util.ver \
+	-version-info "4:4:3"
+
+if WITH_GNUTLS
+libnm_util_la_SOURCES += crypto_gnutls.c
+libnm_util_la_CPPFLAGS += $(LIBGCRYPT_CFLAGS) $(GNUTLS_CFLAGS)
+libnm_util_la_LIBADD += $(LIBGCRYPT_LIBS) $(GNUTLS_LIBS)
+endif
+
+if WITH_NSS
+libnm_util_la_SOURCES += crypto_nss.c
+libnm_util_la_CPPFLAGS += $(NSS_CFLAGS)
+libnm_util_la_LIBADD += $(NSS_LIBS)
+endif
+
+libnm_util_includedir=$(includedir)/NetworkManager
+
+
+###########################################
+# Crypto test library
+###########################################
+
+noinst_LTLIBRARIES = libtest-crypto.la
+
+libtest_crypto_la_SOURCES = crypto.c
+
+libtest_crypto_la_CPPFLAGS = \
+	$(GLIB_CFLAGS)
+
+libtest_crypto_la_LIBADD = \
+	$(GLIB_LIBS)
+
+if WITH_GNUTLS
+libtest_crypto_la_SOURCES += crypto_gnutls.c
+libtest_crypto_la_CPPFLAGS += $(LIBGCRYPT_CFLAGS) $(GNUTLS_CFLAGS)
+libtest_crypto_la_LIBADD += $(LIBGCRYPT_LIBS) $(GNUTLS_LIBS)
+endif
+
+if WITH_NSS
+libtest_crypto_la_SOURCES += crypto_nss.c
+libtest_crypto_la_CPPFLAGS += $(NSS_CFLAGS)
+libtest_crypto_la_LIBADD += $(NSS_LIBS)
+endif
+
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libnm-util.pc
+
+DISTCLEANFILES = libnm-util.pc
+
+EXTRA_DIST = libnm-util.pc.in libnm-util.ver
+
diff --git a/libnm-util/Makefile.in b/libnm-util/Makefile.in
new file mode 100644
index 00000000..2da64da9
--- /dev/null
+++ b/libnm-util/Makefile.in
@@ -0,0 +1,1121 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@WITH_GNUTLS_TRUE@am__append_1 = crypto_gnutls.c
+@WITH_GNUTLS_TRUE@am__append_2 = $(LIBGCRYPT_CFLAGS) $(GNUTLS_CFLAGS)
+@WITH_GNUTLS_TRUE@am__append_3 = $(LIBGCRYPT_LIBS) $(GNUTLS_LIBS)
+@WITH_NSS_TRUE@am__append_4 = crypto_nss.c
+@WITH_NSS_TRUE@am__append_5 = $(NSS_CFLAGS)
+@WITH_NSS_TRUE@am__append_6 = $(NSS_LIBS)
+@WITH_GNUTLS_TRUE@am__append_7 = crypto_gnutls.c
+@WITH_GNUTLS_TRUE@am__append_8 = $(LIBGCRYPT_CFLAGS) $(GNUTLS_CFLAGS)
+@WITH_GNUTLS_TRUE@am__append_9 = $(LIBGCRYPT_LIBS) $(GNUTLS_LIBS)
+@WITH_NSS_TRUE@am__append_10 = crypto_nss.c
+@WITH_NSS_TRUE@am__append_11 = $(NSS_CFLAGS)
+@WITH_NSS_TRUE@am__append_12 = $(NSS_LIBS)
+subdir = libnm-util
+DIST_COMMON = $(libnm_util_include_HEADERS) $(srcdir)/Makefile.am \
+	$(srcdir)/Makefile.in $(srcdir)/libnm-util.pc.in COPYING
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = libnm-util.pc
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
+	"$(DESTDIR)$(libnm_util_includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+@WITH_GNUTLS_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \
+@WITH_GNUTLS_TRUE@	$(am__DEPENDENCIES_1)
+@WITH_NSS_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1)
+libnm_util_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+am__libnm_util_la_SOURCES_DIST = crypto.c crypto.h nm-connection.c \
+	nm-param-spec-specialized.c nm-param-spec-specialized.h \
+	nm-setting.c nm-setting-8021x.c nm-setting-bluetooth.c \
+	nm-setting-connection.c nm-setting-ip4-config.c \
+	nm-setting-ip6-config.c nm-setting-ppp.c nm-setting-pppoe.c \
+	nm-setting-serial.c nm-setting-gsm.c nm-setting-cdma.c \
+	nm-setting-olpc-mesh.c nm-setting-wired.c \
+	nm-setting-wireless.c nm-setting-wireless-security.c \
+	nm-setting-vpn.c nm-utils.c nm-utils-private.h nm-connection.h \
+	nm-setting.h nm-setting-8021x.h nm-setting-bluetooth.h \
+	nm-setting-connection.h nm-setting-ip4-config.h \
+	nm-setting-ip6-config.h nm-setting-ppp.h nm-setting-pppoe.h \
+	nm-setting-serial.h nm-setting-gsm.h nm-setting-cdma.h \
+	nm-setting-olpc-mesh.h nm-setting-wired.h \
+	nm-setting-wireless.h nm-setting-wireless-security.h \
+	nm-setting-vpn.h nm-utils.h crypto_gnutls.c crypto_nss.c
+am__objects_1 =
+@WITH_GNUTLS_TRUE@am__objects_2 = libnm_util_la-crypto_gnutls.lo
+@WITH_NSS_TRUE@am__objects_3 = libnm_util_la-crypto_nss.lo
+am_libnm_util_la_OBJECTS = libnm_util_la-crypto.lo \
+	libnm_util_la-nm-connection.lo \
+	libnm_util_la-nm-param-spec-specialized.lo \
+	libnm_util_la-nm-setting.lo libnm_util_la-nm-setting-8021x.lo \
+	libnm_util_la-nm-setting-bluetooth.lo \
+	libnm_util_la-nm-setting-connection.lo \
+	libnm_util_la-nm-setting-ip4-config.lo \
+	libnm_util_la-nm-setting-ip6-config.lo \
+	libnm_util_la-nm-setting-ppp.lo \
+	libnm_util_la-nm-setting-pppoe.lo \
+	libnm_util_la-nm-setting-serial.lo \
+	libnm_util_la-nm-setting-gsm.lo \
+	libnm_util_la-nm-setting-cdma.lo \
+	libnm_util_la-nm-setting-olpc-mesh.lo \
+	libnm_util_la-nm-setting-wired.lo \
+	libnm_util_la-nm-setting-wireless.lo \
+	libnm_util_la-nm-setting-wireless-security.lo \
+	libnm_util_la-nm-setting-vpn.lo libnm_util_la-nm-utils.lo \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3)
+libnm_util_la_OBJECTS = $(am_libnm_util_la_OBJECTS)
+libnm_util_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(libnm_util_la_LDFLAGS) $(LDFLAGS) -o $@
+libtest_crypto_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
+am__libtest_crypto_la_SOURCES_DIST = crypto.c crypto_gnutls.c \
+	crypto_nss.c
+@WITH_GNUTLS_TRUE@am__objects_4 = libtest_crypto_la-crypto_gnutls.lo
+@WITH_NSS_TRUE@am__objects_5 = libtest_crypto_la-crypto_nss.lo
+am_libtest_crypto_la_OBJECTS = libtest_crypto_la-crypto.lo \
+	$(am__objects_4) $(am__objects_5)
+libtest_crypto_la_OBJECTS = $(am_libtest_crypto_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(libnm_util_la_SOURCES) $(libtest_crypto_la_SOURCES)
+DIST_SOURCES = $(am__libnm_util_la_SOURCES_DIST) \
+	$(am__libtest_crypto_la_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+	html-recursive info-recursive install-data-recursive \
+	install-dvi-recursive install-exec-recursive \
+	install-html-recursive install-info-recursive \
+	install-pdf-recursive install-ps-recursive install-recursive \
+	installcheck-recursive installdirs-recursive pdf-recursive \
+	ps-recursive uninstall-recursive
+DATA = $(pkgconfig_DATA)
+HEADERS = $(libnm_util_include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+	distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = . tests
+INCLUDES = -I${top_srcdir} -I${top_srcdir}/include
+lib_LTLIBRARIES = libnm-util.la
+libnm_util_la_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_CFLAGS) $(UUID_CFLAGS) \
+	-DG_DISABLE_DEPRECATED $(am__append_2) $(am__append_5)
+libnm_util_include_HEADERS = \
+	nm-connection.h			\
+	nm-setting.h			\
+	nm-setting-8021x.h		\
+	nm-setting-bluetooth.h		\
+	nm-setting-connection.h		\
+	nm-setting-ip4-config.h		\
+	nm-setting-ip6-config.h		\
+	nm-setting-ppp.h		\
+	nm-setting-pppoe.h		\
+	nm-setting-serial.h		\
+	nm-setting-gsm.h		\
+	nm-setting-cdma.h		\
+	nm-setting-olpc-mesh.h		\
+	nm-setting-wired.h		\
+	nm-setting-wireless.h		\
+	nm-setting-wireless-security.h	\
+	nm-setting-vpn.h		\
+	nm-utils.h
+
+libnm_util_la_SOURCES = crypto.c crypto.h nm-connection.c \
+	nm-param-spec-specialized.c nm-param-spec-specialized.h \
+	nm-setting.c nm-setting-8021x.c nm-setting-bluetooth.c \
+	nm-setting-connection.c nm-setting-ip4-config.c \
+	nm-setting-ip6-config.c nm-setting-ppp.c nm-setting-pppoe.c \
+	nm-setting-serial.c nm-setting-gsm.c nm-setting-cdma.c \
+	nm-setting-olpc-mesh.c nm-setting-wired.c \
+	nm-setting-wireless.c nm-setting-wireless-security.c \
+	nm-setting-vpn.c nm-utils.c nm-utils-private.h \
+	$(libnm_util_include_HEADERS) $(am__append_1) $(am__append_4)
+libnm_util_la_LIBADD = $(GLIB_LIBS) $(DBUS_LIBS) $(UUID_LIBS) \
+	$(am__append_3) $(am__append_6)
+libnm_util_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnm-util.ver \
+	-version-info "4:4:3"
+
+libnm_util_includedir = $(includedir)/NetworkManager
+
+###########################################
+# Crypto test library
+###########################################
+noinst_LTLIBRARIES = libtest-crypto.la
+libtest_crypto_la_SOURCES = crypto.c $(am__append_7) $(am__append_10)
+libtest_crypto_la_CPPFLAGS = $(GLIB_CFLAGS) $(am__append_8) \
+	$(am__append_11)
+libtest_crypto_la_LIBADD = $(GLIB_LIBS) $(am__append_9) \
+	$(am__append_12)
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libnm-util.pc
+DISTCLEANFILES = libnm-util.pc
+EXTRA_DIST = libnm-util.pc.in libnm-util.ver
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libnm-util/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu libnm-util/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+libnm-util.pc: $(top_builddir)/config.status $(srcdir)/libnm-util.pc.in
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libnm-util.la: $(libnm_util_la_OBJECTS) $(libnm_util_la_DEPENDENCIES) 
+	$(libnm_util_la_LINK) -rpath $(libdir) $(libnm_util_la_OBJECTS) $(libnm_util_la_LIBADD) $(LIBS)
+libtest-crypto.la: $(libtest_crypto_la_OBJECTS) $(libtest_crypto_la_DEPENDENCIES) 
+	$(LINK)  $(libtest_crypto_la_OBJECTS) $(libtest_crypto_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-crypto.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-crypto_gnutls.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-crypto_nss.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-param-spec-specialized.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-8021x.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-cdma.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-gsm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-ip4-config.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-ip6-config.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-olpc-mesh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-ppp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-pppoe.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-serial.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-vpn.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-wired.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-wireless-security.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting-wireless.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-setting.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnm_util_la-nm-utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_crypto_la-crypto.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_crypto_la-crypto_gnutls.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtest_crypto_la-crypto_nss.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+libnm_util_la-crypto.lo: crypto.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-crypto.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-crypto.Tpo -c -o libnm_util_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-crypto.Tpo $(DEPDIR)/libnm_util_la-crypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='crypto.c' object='libnm_util_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+
+libnm_util_la-nm-connection.lo: nm-connection.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-connection.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-connection.Tpo -c -o libnm_util_la-nm-connection.lo `test -f 'nm-connection.c' || echo '$(srcdir)/'`nm-connection.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-connection.Tpo $(DEPDIR)/libnm_util_la-nm-connection.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-connection.c' object='libnm_util_la-nm-connection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-connection.lo `test -f 'nm-connection.c' || echo '$(srcdir)/'`nm-connection.c
+
+libnm_util_la-nm-param-spec-specialized.lo: nm-param-spec-specialized.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-param-spec-specialized.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-param-spec-specialized.Tpo -c -o libnm_util_la-nm-param-spec-specialized.lo `test -f 'nm-param-spec-specialized.c' || echo '$(srcdir)/'`nm-param-spec-specialized.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-param-spec-specialized.Tpo $(DEPDIR)/libnm_util_la-nm-param-spec-specialized.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-param-spec-specialized.c' object='libnm_util_la-nm-param-spec-specialized.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-param-spec-specialized.lo `test -f 'nm-param-spec-specialized.c' || echo '$(srcdir)/'`nm-param-spec-specialized.c
+
+libnm_util_la-nm-setting.lo: nm-setting.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting.Tpo -c -o libnm_util_la-nm-setting.lo `test -f 'nm-setting.c' || echo '$(srcdir)/'`nm-setting.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting.Tpo $(DEPDIR)/libnm_util_la-nm-setting.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting.c' object='libnm_util_la-nm-setting.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting.lo `test -f 'nm-setting.c' || echo '$(srcdir)/'`nm-setting.c
+
+libnm_util_la-nm-setting-8021x.lo: nm-setting-8021x.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-8021x.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-8021x.Tpo -c -o libnm_util_la-nm-setting-8021x.lo `test -f 'nm-setting-8021x.c' || echo '$(srcdir)/'`nm-setting-8021x.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-8021x.Tpo $(DEPDIR)/libnm_util_la-nm-setting-8021x.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-8021x.c' object='libnm_util_la-nm-setting-8021x.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-8021x.lo `test -f 'nm-setting-8021x.c' || echo '$(srcdir)/'`nm-setting-8021x.c
+
+libnm_util_la-nm-setting-bluetooth.lo: nm-setting-bluetooth.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-bluetooth.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-bluetooth.Tpo -c -o libnm_util_la-nm-setting-bluetooth.lo `test -f 'nm-setting-bluetooth.c' || echo '$(srcdir)/'`nm-setting-bluetooth.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-bluetooth.Tpo $(DEPDIR)/libnm_util_la-nm-setting-bluetooth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-bluetooth.c' object='libnm_util_la-nm-setting-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-bluetooth.lo `test -f 'nm-setting-bluetooth.c' || echo '$(srcdir)/'`nm-setting-bluetooth.c
+
+libnm_util_la-nm-setting-connection.lo: nm-setting-connection.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-connection.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-connection.Tpo -c -o libnm_util_la-nm-setting-connection.lo `test -f 'nm-setting-connection.c' || echo '$(srcdir)/'`nm-setting-connection.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-connection.Tpo $(DEPDIR)/libnm_util_la-nm-setting-connection.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-connection.c' object='libnm_util_la-nm-setting-connection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-connection.lo `test -f 'nm-setting-connection.c' || echo '$(srcdir)/'`nm-setting-connection.c
+
+libnm_util_la-nm-setting-ip4-config.lo: nm-setting-ip4-config.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-ip4-config.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-ip4-config.Tpo -c -o libnm_util_la-nm-setting-ip4-config.lo `test -f 'nm-setting-ip4-config.c' || echo '$(srcdir)/'`nm-setting-ip4-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-ip4-config.Tpo $(DEPDIR)/libnm_util_la-nm-setting-ip4-config.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-ip4-config.c' object='libnm_util_la-nm-setting-ip4-config.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-ip4-config.lo `test -f 'nm-setting-ip4-config.c' || echo '$(srcdir)/'`nm-setting-ip4-config.c
+
+libnm_util_la-nm-setting-ip6-config.lo: nm-setting-ip6-config.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-ip6-config.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-ip6-config.Tpo -c -o libnm_util_la-nm-setting-ip6-config.lo `test -f 'nm-setting-ip6-config.c' || echo '$(srcdir)/'`nm-setting-ip6-config.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-ip6-config.Tpo $(DEPDIR)/libnm_util_la-nm-setting-ip6-config.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-ip6-config.c' object='libnm_util_la-nm-setting-ip6-config.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-ip6-config.lo `test -f 'nm-setting-ip6-config.c' || echo '$(srcdir)/'`nm-setting-ip6-config.c
+
+libnm_util_la-nm-setting-ppp.lo: nm-setting-ppp.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-ppp.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-ppp.Tpo -c -o libnm_util_la-nm-setting-ppp.lo `test -f 'nm-setting-ppp.c' || echo '$(srcdir)/'`nm-setting-ppp.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-ppp.Tpo $(DEPDIR)/libnm_util_la-nm-setting-ppp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-ppp.c' object='libnm_util_la-nm-setting-ppp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-ppp.lo `test -f 'nm-setting-ppp.c' || echo '$(srcdir)/'`nm-setting-ppp.c
+
+libnm_util_la-nm-setting-pppoe.lo: nm-setting-pppoe.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-pppoe.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-pppoe.Tpo -c -o libnm_util_la-nm-setting-pppoe.lo `test -f 'nm-setting-pppoe.c' || echo '$(srcdir)/'`nm-setting-pppoe.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-pppoe.Tpo $(DEPDIR)/libnm_util_la-nm-setting-pppoe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-pppoe.c' object='libnm_util_la-nm-setting-pppoe.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-pppoe.lo `test -f 'nm-setting-pppoe.c' || echo '$(srcdir)/'`nm-setting-pppoe.c
+
+libnm_util_la-nm-setting-serial.lo: nm-setting-serial.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-serial.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-serial.Tpo -c -o libnm_util_la-nm-setting-serial.lo `test -f 'nm-setting-serial.c' || echo '$(srcdir)/'`nm-setting-serial.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-serial.Tpo $(DEPDIR)/libnm_util_la-nm-setting-serial.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-serial.c' object='libnm_util_la-nm-setting-serial.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-serial.lo `test -f 'nm-setting-serial.c' || echo '$(srcdir)/'`nm-setting-serial.c
+
+libnm_util_la-nm-setting-gsm.lo: nm-setting-gsm.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-gsm.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-gsm.Tpo -c -o libnm_util_la-nm-setting-gsm.lo `test -f 'nm-setting-gsm.c' || echo '$(srcdir)/'`nm-setting-gsm.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-gsm.Tpo $(DEPDIR)/libnm_util_la-nm-setting-gsm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-gsm.c' object='libnm_util_la-nm-setting-gsm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-gsm.lo `test -f 'nm-setting-gsm.c' || echo '$(srcdir)/'`nm-setting-gsm.c
+
+libnm_util_la-nm-setting-cdma.lo: nm-setting-cdma.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-cdma.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-cdma.Tpo -c -o libnm_util_la-nm-setting-cdma.lo `test -f 'nm-setting-cdma.c' || echo '$(srcdir)/'`nm-setting-cdma.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-cdma.Tpo $(DEPDIR)/libnm_util_la-nm-setting-cdma.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-cdma.c' object='libnm_util_la-nm-setting-cdma.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-cdma.lo `test -f 'nm-setting-cdma.c' || echo '$(srcdir)/'`nm-setting-cdma.c
+
+libnm_util_la-nm-setting-olpc-mesh.lo: nm-setting-olpc-mesh.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-olpc-mesh.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-olpc-mesh.Tpo -c -o libnm_util_la-nm-setting-olpc-mesh.lo `test -f 'nm-setting-olpc-mesh.c' || echo '$(srcdir)/'`nm-setting-olpc-mesh.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-olpc-mesh.Tpo $(DEPDIR)/libnm_util_la-nm-setting-olpc-mesh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-olpc-mesh.c' object='libnm_util_la-nm-setting-olpc-mesh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-olpc-mesh.lo `test -f 'nm-setting-olpc-mesh.c' || echo '$(srcdir)/'`nm-setting-olpc-mesh.c
+
+libnm_util_la-nm-setting-wired.lo: nm-setting-wired.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-wired.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-wired.Tpo -c -o libnm_util_la-nm-setting-wired.lo `test -f 'nm-setting-wired.c' || echo '$(srcdir)/'`nm-setting-wired.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-wired.Tpo $(DEPDIR)/libnm_util_la-nm-setting-wired.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-wired.c' object='libnm_util_la-nm-setting-wired.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-wired.lo `test -f 'nm-setting-wired.c' || echo '$(srcdir)/'`nm-setting-wired.c
+
+libnm_util_la-nm-setting-wireless.lo: nm-setting-wireless.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-wireless.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-wireless.Tpo -c -o libnm_util_la-nm-setting-wireless.lo `test -f 'nm-setting-wireless.c' || echo '$(srcdir)/'`nm-setting-wireless.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-wireless.Tpo $(DEPDIR)/libnm_util_la-nm-setting-wireless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-wireless.c' object='libnm_util_la-nm-setting-wireless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-wireless.lo `test -f 'nm-setting-wireless.c' || echo '$(srcdir)/'`nm-setting-wireless.c
+
+libnm_util_la-nm-setting-wireless-security.lo: nm-setting-wireless-security.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-wireless-security.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-wireless-security.Tpo -c -o libnm_util_la-nm-setting-wireless-security.lo `test -f 'nm-setting-wireless-security.c' || echo '$(srcdir)/'`nm-setting-wireless-security.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-wireless-security.Tpo $(DEPDIR)/libnm_util_la-nm-setting-wireless-security.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-wireless-security.c' object='libnm_util_la-nm-setting-wireless-security.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-wireless-security.lo `test -f 'nm-setting-wireless-security.c' || echo '$(srcdir)/'`nm-setting-wireless-security.c
+
+libnm_util_la-nm-setting-vpn.lo: nm-setting-vpn.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-setting-vpn.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-setting-vpn.Tpo -c -o libnm_util_la-nm-setting-vpn.lo `test -f 'nm-setting-vpn.c' || echo '$(srcdir)/'`nm-setting-vpn.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-setting-vpn.Tpo $(DEPDIR)/libnm_util_la-nm-setting-vpn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-setting-vpn.c' object='libnm_util_la-nm-setting-vpn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-setting-vpn.lo `test -f 'nm-setting-vpn.c' || echo '$(srcdir)/'`nm-setting-vpn.c
+
+libnm_util_la-nm-utils.lo: nm-utils.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-nm-utils.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-nm-utils.Tpo -c -o libnm_util_la-nm-utils.lo `test -f 'nm-utils.c' || echo '$(srcdir)/'`nm-utils.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-nm-utils.Tpo $(DEPDIR)/libnm_util_la-nm-utils.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='nm-utils.c' object='libnm_util_la-nm-utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-nm-utils.lo `test -f 'nm-utils.c' || echo '$(srcdir)/'`nm-utils.c
+
+libnm_util_la-crypto_gnutls.lo: crypto_gnutls.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-crypto_gnutls.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-crypto_gnutls.Tpo -c -o libnm_util_la-crypto_gnutls.lo `test -f 'crypto_gnutls.c' || echo '$(srcdir)/'`crypto_gnutls.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-crypto_gnutls.Tpo $(DEPDIR)/libnm_util_la-crypto_gnutls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='crypto_gnutls.c' object='libnm_util_la-crypto_gnutls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-crypto_gnutls.lo `test -f 'crypto_gnutls.c' || echo '$(srcdir)/'`crypto_gnutls.c
+
+libnm_util_la-crypto_nss.lo: crypto_nss.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnm_util_la-crypto_nss.lo -MD -MP -MF $(DEPDIR)/libnm_util_la-crypto_nss.Tpo -c -o libnm_util_la-crypto_nss.lo `test -f 'crypto_nss.c' || echo '$(srcdir)/'`crypto_nss.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libnm_util_la-crypto_nss.Tpo $(DEPDIR)/libnm_util_la-crypto_nss.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='crypto_nss.c' object='libnm_util_la-crypto_nss.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnm_util_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnm_util_la-crypto_nss.lo `test -f 'crypto_nss.c' || echo '$(srcdir)/'`crypto_nss.c
+
+libtest_crypto_la-crypto.lo: crypto.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_crypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_crypto_la-crypto.lo -MD -MP -MF $(DEPDIR)/libtest_crypto_la-crypto.Tpo -c -o libtest_crypto_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_crypto_la-crypto.Tpo $(DEPDIR)/libtest_crypto_la-crypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='crypto.c' object='libtest_crypto_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_crypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_crypto_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+
+libtest_crypto_la-crypto_gnutls.lo: crypto_gnutls.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_crypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_crypto_la-crypto_gnutls.lo -MD -MP -MF $(DEPDIR)/libtest_crypto_la-crypto_gnutls.Tpo -c -o libtest_crypto_la-crypto_gnutls.lo `test -f 'crypto_gnutls.c' || echo '$(srcdir)/'`crypto_gnutls.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_crypto_la-crypto_gnutls.Tpo $(DEPDIR)/libtest_crypto_la-crypto_gnutls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='crypto_gnutls.c' object='libtest_crypto_la-crypto_gnutls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_crypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_crypto_la-crypto_gnutls.lo `test -f 'crypto_gnutls.c' || echo '$(srcdir)/'`crypto_gnutls.c
+
+libtest_crypto_la-crypto_nss.lo: crypto_nss.c
+@am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_crypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtest_crypto_la-crypto_nss.lo -MD -MP -MF $(DEPDIR)/libtest_crypto_la-crypto_nss.Tpo -c -o libtest_crypto_la-crypto_nss.lo `test -f 'crypto_nss.c' || echo '$(srcdir)/'`crypto_nss.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libtest_crypto_la-crypto_nss.Tpo $(DEPDIR)/libtest_crypto_la-crypto_nss.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='crypto_nss.c' object='libtest_crypto_la-crypto_nss.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libtest_crypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtest_crypto_la-crypto_nss.lo `test -f 'crypto_nss.c' || echo '$(srcdir)/'`crypto_nss.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-pkgconfigDATA: $(pkgconfig_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)"
+	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+	done
+
+uninstall-pkgconfigDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(pkgconfigdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(pkgconfigdir)" && rm -f $$files
+install-libnm_util_includeHEADERS: $(libnm_util_include_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(libnm_util_includedir)" || $(MKDIR_P) "$(DESTDIR)$(libnm_util_includedir)"
+	@list='$(libnm_util_include_HEADERS)'; test -n "$(libnm_util_includedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libnm_util_includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(libnm_util_includedir)" || exit $$?; \
+	done
+
+uninstall-libnm_util_includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(libnm_util_include_HEADERS)'; test -n "$(libnm_util_includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(libnm_util_includedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(libnm_util_includedir)" && rm -f $$files
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(MKDIR_P) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+	for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(libnm_util_includedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+	clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-libnm_util_includeHEADERS \
+	install-pkgconfigDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+	uninstall-libnm_util_includeHEADERS uninstall-pkgconfigDATA
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
+	install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+	all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \
+	ctags ctags-recursive distclean distclean-compile \
+	distclean-generic distclean-libtool distclean-tags distdir dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-libLTLIBRARIES \
+	install-libnm_util_includeHEADERS install-man install-pdf \
+	install-pdf-am install-pkgconfigDATA install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+	uninstall uninstall-am uninstall-libLTLIBRARIES \
+	uninstall-libnm_util_includeHEADERS uninstall-pkgconfigDATA
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libnm-util/crypto.c b/libnm-util/crypto.c
new file mode 100644
index 00000000..813cbc8e
--- /dev/null
+++ b/libnm-util/crypto.c
@@ -0,0 +1,636 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include "crypto.h"
+
+GQuark
+_nm_crypto_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-crypto-error-quark");
+	return quark;
+}
+
+
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_rsa_key_end = "-----END RSA PRIVATE KEY-----";
+
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_dsa_key_end = "-----END DSA PRIVATE KEY-----";
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+
+static const char *
+find_tag (const char *tag, const char *buf, gsize len)
+{
+	gsize i, taglen;
+
+	taglen = strlen (tag);
+	if (len < taglen)
+		return NULL;
+
+	for (i = 0; i < len - taglen + 1; i++) {
+		if (memcmp (buf + i, tag, taglen) == 0)
+			return buf + i;
+	}
+	return NULL;
+}
+
+#define DEK_INFO_TAG "DEK-Info: "
+#define PROC_TYPE_TAG "Proc-Type: "
+
+static GByteArray *
+parse_old_openssl_key_file (GByteArray *contents,
+                            int key_type,
+                            char **out_cipher,
+                            char **out_iv,
+                            GError **error)
+{
+	GByteArray *bindata = NULL;
+	char **lines = NULL;
+	char **ln = NULL;
+	const char *pos;
+	const char *end;
+	GString *str = NULL;
+	int enc_tags = 0;
+	char *iv = NULL;
+	char *cipher = NULL;
+	unsigned char *tmp = NULL;
+	gsize tmp_len = 0;
+	const char *start_tag;
+	const char *end_tag;
+
+	switch (key_type) {
+	case NM_CRYPTO_KEY_TYPE_RSA:
+		start_tag = pem_rsa_key_begin;
+		end_tag = pem_rsa_key_end;
+		break;
+	case NM_CRYPTO_KEY_TYPE_DSA:
+		start_tag = pem_dsa_key_begin;
+		end_tag = pem_dsa_key_end;
+		break;
+	default:
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+		             "Unknown key type %d",
+		             key_type);
+		g_assert_not_reached ();
+		return NULL;
+	}
+
+	pos = find_tag (start_tag, (const char *) contents->data, contents->len);
+	if (!pos)
+		goto parse_error;
+
+	pos += strlen (start_tag);
+
+	end = find_tag (end_tag, pos, (const char *) contents->data + contents->len - pos);
+	if (end == NULL) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+		             _("PEM key file had no end tag '%s'."),
+		             end_tag);
+		goto parse_error;
+	}
+	*((char *) end) = '\0';
+
+	lines = g_strsplit (pos, "\n", 0);
+	if (!lines || g_strv_length (lines) <= 1) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+		             _("Doesn't look like a PEM private key file."));
+		goto parse_error;
+	}
+
+	str = g_string_new_len (NULL, end - pos);
+	if (!str) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store PEM file data."));
+		goto parse_error;
+	}
+
+	for (ln = lines; *ln; ln++) {
+		char *p = *ln;
+
+		/* Chug leading spaces */
+		p = g_strstrip (p);
+		if (!*p)
+			continue;
+
+		if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
+			if (enc_tags++ != 0) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+				             _("Malformed PEM file: Proc-Type was not first tag."));
+				goto parse_error;
+			}
+
+			p += strlen (PROC_TYPE_TAG);
+			if (strcmp (p, "4,ENCRYPTED")) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+				             _("Malformed PEM file: unknown Proc-Type tag '%s'."),
+				             p);
+				goto parse_error;
+			}
+		} else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
+			char *comma;
+
+			if (enc_tags++ != 1) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+				             _("Malformed PEM file: DEK-Info was not the second tag."));
+				goto parse_error;
+			}
+
+			p += strlen (DEK_INFO_TAG);
+
+			/* Grab the IV first */
+			comma = strchr (p, ',');
+			if (!comma || (*(comma + 1) == '\0')) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+				             _("Malformed PEM file: no IV found in DEK-Info tag."));
+				goto parse_error;
+			}
+			*comma++ = '\0';
+			if (!g_ascii_isxdigit (*comma)) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+				             _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
+				goto parse_error;
+			}
+			iv = g_strdup (comma);
+
+			/* Get the private key cipher */
+			if (!strcasecmp (p, "DES-EDE3-CBC")) {
+				cipher = g_strdup (p);
+			} else if (!strcasecmp (p, "DES-CBC")) {
+				cipher = g_strdup (p);
+			} else {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+				             _("Malformed PEM file: unknown private key cipher '%s'."),
+				             p);
+				goto parse_error;
+			}
+		} else {
+			if ((enc_tags != 0) && (enc_tags != 2)) {
+				g_set_error (error, NM_CRYPTO_ERROR,
+				             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+				             "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
+				goto parse_error;
+			}
+			g_string_append (str, p);
+		}
+	}
+
+	tmp = g_base64_decode (str->str, &tmp_len);
+	if (tmp == NULL || !tmp_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_DECODE_FAILED,
+		             _("Could not decode private key."));
+		goto parse_error;
+	}
+
+	if (lines)
+		g_strfreev (lines);
+
+	bindata = g_byte_array_sized_new (tmp_len);
+	g_byte_array_append (bindata, tmp, tmp_len);
+	*out_iv = iv;
+	*out_cipher = cipher;
+	return bindata;
+
+parse_error:
+	g_free (cipher);
+	g_free (iv);
+	if (lines)
+		g_strfreev (lines);
+	return NULL;
+}
+
+static GByteArray *
+file_to_g_byte_array (const char *filename,
+                      gboolean privkey,
+                      GError **error)
+{
+	char *contents, *der = NULL;
+	GByteArray *array = NULL;
+	gsize length = 0;
+	const char *pos = NULL;
+
+	if (!g_file_get_contents (filename, &contents, &length, error))
+		return NULL;
+
+	if (!privkey)
+		pos = find_tag (pem_cert_begin, contents, length);
+
+	if (pos) {
+		const char *end;
+
+		pos += strlen (pem_cert_begin);
+		end = find_tag (pem_cert_end, pos, contents + length - pos);
+		if (end == NULL) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+			             _("PEM certificate '%s' had no end tag '%s'."),
+			             filename, pem_cert_end);
+			goto done;
+		}
+
+		contents[end - contents - 1] = '\0';
+		der = (char *) g_base64_decode (pos, &length);
+		if (der == NULL || !length) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_DECODE_FAILED,
+			             _("Failed to decode certificate."));
+			goto done;
+		}
+	}
+
+	array = g_byte_array_sized_new (length);
+	if (!array) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store certificate data."));
+		goto done;
+	}
+
+	g_byte_array_append (array, der ? (unsigned char *) der : (unsigned char *) contents, length);
+	if (array->len != length) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store file data."));
+		g_byte_array_free (array, TRUE);
+		array = NULL;
+	}
+
+done:
+	g_free (der);
+	g_free (contents);
+	return array;
+}
+
+/*
+ * Convert a hex string into bytes.
+ */
+static char *
+convert_iv (const char *src,
+            gsize *out_len,
+            GError **error)
+{
+	int num;
+	int i;
+	char conv[3];
+	char *c;
+
+	g_return_val_if_fail (src != NULL, NULL);
+
+	num = strlen (src);
+	if (num % 2) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_RAW_IV_INVALID,
+		             _("IV must be an even number of bytes in length."));
+		return NULL;
+	}
+
+	num /= 2;
+	c = g_malloc0 (num + 1);
+	if (c == NULL) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store the IV."));
+        return NULL;
+	}
+
+	conv[2] = '\0';
+	for (i = 0; i < num; i++) {
+        conv[0] = src[(i * 2)];
+        conv[1] = src[(i * 2) + 1];
+		if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_RAW_IV_INVALID,
+			             _("IV contains non-hexadecimal digits."));
+			goto error;
+		}
+
+		c[i] = strtol(conv, NULL, 16);
+	}
+	*out_len = num;
+	return c;
+
+error:
+	g_free (c);
+	return NULL;
+}
+
+static char *
+make_des_key (const char *cipher,
+              const char *salt,
+              const gsize salt_len,
+              const char *password,
+              gsize *out_len,
+              GError **error)
+{
+	char *key;
+	guint32 digest_len;
+
+	g_return_val_if_fail (cipher != NULL, NULL);
+	g_return_val_if_fail (salt != NULL, NULL);
+	g_return_val_if_fail (salt_len >= 8, NULL);
+	g_return_val_if_fail (password != NULL, NULL);
+	g_return_val_if_fail (out_len != NULL, NULL);
+
+	if (!strcmp (cipher, "DES-EDE3-CBC"))
+		digest_len = 24;
+	else if (!strcmp (cipher, "DES-CBC"))
+		digest_len = 8;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	key = g_malloc0 (digest_len + 1);
+	if (!key) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to decrypt private key."));
+		return NULL;
+	}
+
+	if (!crypto_md5_hash (salt,
+	                      salt_len,
+	                      password,
+	                      strlen (password),
+	                      key,
+	                      digest_len,
+	                      error))
+		goto error;
+
+	*out_len = digest_len;
+	return key;
+
+error:
+	if (key) {
+		/* Don't leak stale key material */
+		memset (key, 0, digest_len);
+		g_free (key);
+	}
+	return NULL;
+}
+
+static char *
+decrypt_key (const char *cipher,
+             int key_type,
+             GByteArray *data,
+             const char *iv,
+             const char *password,
+             gsize *out_len,
+             GError **error)
+{
+	char *bin_iv = NULL;
+	gsize bin_iv_len = 0;
+	char *key = NULL;
+	gsize key_len = 0;
+	char *output = NULL;
+
+	bin_iv = convert_iv (iv, &bin_iv_len, error);
+	if (!bin_iv)
+		return NULL;
+
+	/* Convert the PIN and IV into a DES key */
+	key = make_des_key (cipher, bin_iv, bin_iv_len, password, &key_len, error);
+	if (!key || !key_len)
+		goto out;
+
+	output = crypto_decrypt (cipher, key_type,
+	                         data,
+	                         bin_iv, bin_iv_len,
+	                         key, key_len,
+	                         out_len,
+	                         error);
+	if (!output)
+		goto out;
+
+	if (*out_len == 0) {
+		g_free (output);
+		output = NULL;
+		goto out;
+	}
+ 
+out:
+	if (key) {
+		/* Don't leak stale key material */
+		memset (key, 0, key_len);
+		g_free (key);
+	}
+	g_free (bin_iv);
+	return output;
+}
+
+GByteArray *
+crypto_get_private_key_data (GByteArray *contents,
+                             const char *password,
+                             NMCryptoKeyType *out_key_type,
+                             NMCryptoFileFormat *out_file_type,
+                             GError **error)
+{
+	GByteArray *array = NULL;
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_RSA;
+	GByteArray *data;
+	char *iv = NULL;
+	char *cipher = NULL;
+	char *decrypted = NULL;
+	gsize decrypted_len = 0;
+
+	g_return_val_if_fail (contents != NULL, NULL);
+	g_return_val_if_fail (password != NULL, NULL);
+	g_return_val_if_fail (out_key_type != NULL, NULL);
+	g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
+	g_return_val_if_fail (out_file_type != NULL, NULL);
+	g_return_val_if_fail (*out_file_type == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
+
+	/* Try PKCS#12 first */
+	if (crypto_verify_pkcs12 (contents, password, NULL)) {
+		*out_key_type = NM_CRYPTO_KEY_TYPE_ENCRYPTED;
+		*out_file_type = NM_CRYPTO_FILE_FORMAT_PKCS12;
+
+		array = g_byte_array_sized_new (contents->len);
+		g_byte_array_append (array, contents->data, contents->len);
+		return array;
+	}
+
+	/* OpenSSL non-standard legacy PEM files */
+
+	/* Try RSA keys first */
+	data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
+	if (!data) {
+		g_clear_error (error);
+
+		/* DSA next */
+		key_type = NM_CRYPTO_KEY_TYPE_DSA;
+		data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
+		if (!data) {
+			g_clear_error (error);
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+			             _("Unable to determine private key type."));
+			goto out;
+		}
+	}
+
+	decrypted = decrypt_key (cipher,
+	                         key_type,
+	                         data,
+	                         iv,
+	                         password,
+	                         &decrypted_len,
+	                         error);
+	if (!decrypted)
+		goto out;
+
+	array = g_byte_array_sized_new (decrypted_len);
+	if (!array) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to store decrypted private key."));
+		goto out;
+	}
+
+	g_byte_array_append (array, (const guint8 *) decrypted, decrypted_len);
+	*out_key_type = key_type;
+	*out_file_type = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+
+out:
+	if (decrypted) {
+		/* Don't expose key material */
+		memset (decrypted, 0, decrypted_len);
+		g_free (decrypted);
+	}
+	if (data)
+		g_byte_array_free (data, TRUE);
+	g_free (cipher);
+	g_free (iv);
+	return array;
+}
+
+GByteArray *
+crypto_get_private_key (const char *file,
+                        const char *password,
+                        NMCryptoKeyType *out_key_type,
+                        NMCryptoFileFormat *out_file_type,
+                        GError **error)
+{
+	GByteArray *contents;
+	GByteArray *key = NULL;
+
+	contents = file_to_g_byte_array (file, TRUE, error);
+	if (contents) {
+		key = crypto_get_private_key_data (contents, password, out_key_type, out_file_type, error);
+		g_byte_array_free (contents, TRUE);
+	}
+	return key;
+}
+
+GByteArray *
+crypto_load_and_verify_certificate (const char *file,
+                                    NMCryptoFileFormat *out_file_format,
+                                    GError **error)
+{
+	GByteArray *array;
+
+	g_return_val_if_fail (file != NULL, NULL);
+	g_return_val_if_fail (out_file_format != NULL, NULL);
+	g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
+
+	array = file_to_g_byte_array (file, FALSE, error);
+	if (!array)
+		return NULL;
+
+	*out_file_format = crypto_verify_cert (array->data, array->len, error);
+	if (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
+		/* Try PKCS#12 */
+		if (crypto_is_pkcs12_data (array)) {
+			*out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
+			g_clear_error (error);
+		} else {
+			g_byte_array_free (array, TRUE);
+			array = NULL;
+		}
+	}
+
+	return array;
+}
+
+gboolean
+crypto_is_pkcs12_data (const GByteArray *data)
+{
+	GError *error = NULL;
+	gboolean success;
+
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	success = crypto_verify_pkcs12 (data, NULL, &error);
+	if (success)
+		return TRUE;
+
+	/* If the error was just a decryption error, then it's pkcs#12 */
+	if (error) {
+		if (g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
+			success = TRUE;
+		g_error_free (error);		
+	}
+
+	return success;
+}
+
+gboolean
+crypto_is_pkcs12_file (const char *file, GError **error)
+{
+	GByteArray *contents;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (file != NULL, FALSE);
+
+	contents = file_to_g_byte_array (file, TRUE, error);
+	if (contents) {
+		success = crypto_is_pkcs12_data (contents);
+		g_byte_array_free (contents, TRUE);
+	}
+	return success;
+}
+
diff --git a/libnm-util/crypto.h b/libnm-util/crypto.h
new file mode 100644
index 00000000..38471cea
--- /dev/null
+++ b/libnm-util/crypto.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ */
+
+#include <glib.h>
+
+#define MD5_HASH_LEN 20
+#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC"
+#define CIPHER_DES_CBC "DES-CBC"
+
+enum {
+	NM_CRYPTO_ERR_NONE = 0,
+	NM_CRYPTO_ERR_INIT_FAILED,
+	NM_CRYPTO_ERR_CANT_READ_FILE,
+	NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+	NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+	NM_CRYPTO_ERR_DECODE_FAILED,
+	NM_CRYPTO_ERR_OUT_OF_MEMORY,
+	NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+	NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+	NM_CRYPTO_ERR_RAW_IV_INVALID,
+	NM_CRYPTO_ERR_MD5_INIT_FAILED,
+	NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+	NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+	NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+	NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+	NM_CRYPTO_ERR_INVALID_PASSWORD,
+	NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED,
+	NM_CRYPTO_ERR_RANDOMIZE_FAILED
+};
+
+typedef enum {
+	NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
+	NM_CRYPTO_KEY_TYPE_RSA,
+	NM_CRYPTO_KEY_TYPE_DSA,
+	NM_CRYPTO_KEY_TYPE_ENCRYPTED
+} NMCryptoKeyType;
+
+typedef enum {
+	NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
+	NM_CRYPTO_FILE_FORMAT_X509,
+	NM_CRYPTO_FILE_FORMAT_RAW_KEY,
+	NM_CRYPTO_FILE_FORMAT_PKCS12
+} NMCryptoFileFormat;
+
+#define NM_CRYPTO_ERROR _nm_crypto_error_quark ()
+GQuark _nm_crypto_error_quark (void);
+
+gboolean crypto_init (GError **error);
+
+void crypto_deinit (void);
+
+GByteArray * crypto_get_private_key_data (GByteArray *contents,
+                                          const char *password,
+                                          NMCryptoKeyType *out_key_type,
+                                          NMCryptoFileFormat *out_file_format,
+                                          GError **error);
+
+GByteArray * crypto_get_private_key (const char *file,
+                                     const char *password,
+                                     NMCryptoKeyType *out_key_type,
+                                     NMCryptoFileFormat *out_file_format,
+                                     GError **error);
+
+GByteArray * crypto_load_and_verify_certificate (const char *file,
+                                                 NMCryptoFileFormat *out_file_format,
+                                                 GError **error);
+
+gboolean crypto_is_pkcs12_file (const char *file, GError **error);
+
+gboolean crypto_is_pkcs12_data (const GByteArray *data);
+
+
+/* Internal utils API bits for crypto providers */
+
+gboolean crypto_md5_hash (const char *salt,
+                          const gsize salt_len,
+                          const char *password,
+                          gsize password_len,
+                          char *buffer,
+                          gsize buflen,
+                          GError **error);
+
+char * crypto_decrypt (const char *cipher,
+                       int key_type,
+                       GByteArray *data,
+                       const char *iv,
+                       const gsize iv_len,
+                       const char *key,
+                       const gsize key_len,
+                       gsize *out_len,
+                       GError **error);
+
+char * crypto_encrypt (const char *cipher,
+                       const GByteArray *data,
+                       const char *iv,
+                       gsize iv_len,
+                       const char *key,
+                       gsize key_len,
+                       gsize *out_len,
+                       GError **error);
+
+gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error);
+
+NMCryptoFileFormat crypto_verify_cert (const unsigned char *data,
+                                       gsize len,
+                                       GError **error);
+
+gboolean crypto_verify_pkcs12 (const GByteArray *data,
+                               const char *password,
+                               GError **error);
+
diff --git a/libnm-util/crypto_gnutls.c b/libnm-util/crypto_gnutls.c
new file mode 100644
index 00000000..edfc16af
--- /dev/null
+++ b/libnm-util/crypto_gnutls.c
@@ -0,0 +1,448 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/pkcs12.h>
+
+#include "crypto.h"
+
+#define SALT_LEN 8
+
+static gboolean initialized = FALSE;
+
+gboolean
+crypto_init (GError **error)
+{
+	if (initialized)
+		return TRUE;
+
+	if (gnutls_global_init() != 0) {
+		gnutls_global_deinit();
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_INIT_FAILED,
+		             "%s",
+		             _("Failed to initialize the crypto engine."));
+		return FALSE;
+	}
+
+	initialized = TRUE;
+	return TRUE;
+}
+
+void
+crypto_deinit (void)
+{
+	if (initialized)
+		gnutls_global_deinit();
+}
+
+gboolean
+crypto_md5_hash (const char *salt,
+                 const gsize salt_len,
+                 const char *password,
+                 gsize password_len,
+                 char *buffer,
+                 gsize buflen,
+                 GError **error)
+{
+	gcry_md_hd_t ctx;
+	gcry_error_t err;
+	int nkey = buflen;
+	const gsize digest_len = 16;
+	int count = 0;
+	char digest[MD5_HASH_LEN];
+	char *p = buffer;
+
+	if (salt)
+		g_return_val_if_fail (salt_len >= SALT_LEN, FALSE);
+
+	g_return_val_if_fail (password != NULL, FALSE);
+	g_return_val_if_fail (password_len > 0, FALSE);
+	g_return_val_if_fail (buffer != NULL, FALSE);
+	g_return_val_if_fail (buflen > 0, FALSE);
+
+	err = gcry_md_open (&ctx, GCRY_MD_MD5, 0);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_MD5_INIT_FAILED,
+		             _("Failed to initialize the MD5 engine: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		return FALSE;
+	}
+
+	while (nkey > 0) {
+		int i = 0;
+
+		if (count++)
+			gcry_md_write (ctx, digest, digest_len);
+		gcry_md_write (ctx, password, password_len);
+		if (salt)
+			gcry_md_write (ctx, salt, SALT_LEN); /* Only use 8 bytes of salt */
+		gcry_md_final (ctx);
+		memcpy (digest, gcry_md_read (ctx, 0), digest_len);
+		gcry_md_reset (ctx);
+		
+		while (nkey && (i < digest_len)) {
+			*(p++) = digest[i++];
+			nkey--;
+		}
+	}
+
+	memset (digest, 0, sizeof (digest));
+	gcry_md_close (ctx);
+	return TRUE;
+}
+
+char *
+crypto_decrypt (const char *cipher,
+                int key_type,
+                GByteArray *data,
+                const char *iv,
+                const gsize iv_len,
+                const char *key,
+                const gsize key_len,
+                gsize *out_len,
+                GError **error)
+{
+	gcry_cipher_hd_t ctx;
+	gcry_error_t err;
+	int cipher_mech, i;
+	char *output = NULL;
+	gboolean success = FALSE;
+	gsize pad_len, real_iv_len;
+
+	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
+		cipher_mech = GCRY_CIPHER_3DES;
+		real_iv_len = SALT_LEN;
+	} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
+		cipher_mech = GCRY_CIPHER_DES;
+		real_iv_len = SALT_LEN;
+	} else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	if (iv_len < real_iv_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_RAW_IV_INVALID,
+		             _("Invalid IV length (must be at least %zd)."),
+		             real_iv_len);
+		return NULL;
+	}
+
+	output = g_malloc0 (data->len);
+	if (!output) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory for decrypted key buffer."));
+		return NULL;
+	}
+
+	err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the decryption cipher context: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_setkey (ctx, key, key_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+		             _("Failed to set symmetric key for decryption: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_setiv (ctx, iv, iv_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+		             _("Failed to set IV for decryption: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_decrypt (ctx, output, data->len, data->data, data->len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+	pad_len = output[data->len - 1];
+
+	/* Check if the padding at the end of the decrypted data is valid */
+	if (pad_len == 0 || pad_len > real_iv_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: unexpected padding length."));
+		goto out;
+	}
+
+	/* Validate tail padding; last byte is the padding size, and all pad bytes
+	 * should contain the padding size.
+	 */
+	for (i = 1; i <= pad_len; ++i) {
+		if (output[data->len - i] != pad_len) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+			             _("Failed to decrypt the private key."));
+			goto out;
+		}
+	}
+
+	*out_len = data->len - pad_len;
+	success = TRUE;
+
+out:
+	if (!success) {
+		if (output) {
+			/* Don't expose key material */
+			memset (output, 0, data->len);
+			g_free (output);
+			output = NULL;
+		}
+	}
+	gcry_cipher_close (ctx);
+	return output;
+}
+
+char *
+crypto_encrypt (const char *cipher,
+                const GByteArray *data,
+                const char *iv,
+                const gsize iv_len,
+                const char *key,
+                gsize key_len,
+                gsize *out_len,
+                GError **error)
+{
+	gcry_cipher_hd_t ctx;
+	gcry_error_t err;
+	int cipher_mech;
+	char *output = NULL;
+	gboolean success = FALSE;
+	gsize padded_buf_len, pad_len, output_len;
+	char *padded_buf = NULL;
+	guint32 i;
+
+	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
+		cipher_mech = GCRY_CIPHER_3DES;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	/* If data->len % ivlen == 0, then we add another complete block
+	 * onto the end so that the decrypter knows there's padding.
+	 */
+	pad_len = iv_len - (data->len % iv_len);
+	output_len = padded_buf_len = data->len + pad_len;
+	padded_buf = g_malloc0 (padded_buf_len);
+
+	memcpy (padded_buf, data->data, data->len);
+	for (i = 0; i < pad_len; i++)
+		padded_buf[data->len + i] = (guint8) (pad_len & 0xFF);
+
+	output = g_malloc0 (output_len);
+	if (!output) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Could not allocate memory for encrypting."));
+		return NULL;
+	}
+
+	err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the encryption cipher context: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_setkey (ctx, key, key_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+		             _("Failed to set symmetric key for encryption: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	/* gcrypt only wants 8 bytes of the IV (same as the DES block length) */
+	err = gcry_cipher_setiv (ctx, iv, SALT_LEN);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+		             _("Failed to set IV for encryption: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	err = gcry_cipher_encrypt (ctx, output, output_len, padded_buf, padded_buf_len);
+	if (err) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to encrypt the data: %s / %s."),
+		             gcry_strsource (err), gcry_strerror (err));
+		goto out;
+	}
+
+	*out_len = output_len;
+	success = TRUE;
+
+out:
+	if (padded_buf) {
+		memset (padded_buf, 0, padded_buf_len);
+		g_free (padded_buf);
+		padded_buf = NULL;
+	}
+
+	if (!success) {
+		if (output) {
+			/* Don't expose key material */
+			memset (output, 0, output_len);
+			g_free (output);
+			output = NULL;
+		}
+	}
+	gcry_cipher_close (ctx);
+	return output;
+}
+
+NMCryptoFileFormat
+crypto_verify_cert (const unsigned char *data,
+                    gsize len,
+                    GError **error)
+{
+	gnutls_x509_crt_t der;
+	gnutls_datum dt;
+	int err;
+
+	err = gnutls_x509_crt_init (&der);
+	if (err < 0) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+		             _("Error initializing certificate data: %s"),
+		             gnutls_strerror (err));
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	}
+
+	/* Try DER first */
+	dt.data = (unsigned char *) data;
+	dt.size = len;
+	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
+	if (err == GNUTLS_E_SUCCESS) {
+		gnutls_x509_crt_deinit (der);
+		return NM_CRYPTO_FILE_FORMAT_X509;
+	}
+
+	/* And PEM next */
+	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
+	gnutls_x509_crt_deinit (der);
+	if (err == GNUTLS_E_SUCCESS)
+		return NM_CRYPTO_FILE_FORMAT_X509;
+
+	g_set_error (error, NM_CRYPTO_ERROR,
+	             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+	             _("Couldn't decode certificate: %s"),
+	             gnutls_strerror (err));
+	return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+}
+
+gboolean
+crypto_verify_pkcs12 (const GByteArray *data,
+                      const char *password,
+                      GError **error)
+{
+	gnutls_pkcs12_t p12;
+	gnutls_datum dt;
+	gboolean success = FALSE;
+	int err;
+
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	dt.data = (unsigned char *) data->data;
+	dt.size = data->len;
+
+	err = gnutls_pkcs12_init (&p12);
+	if (err < 0) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_DECODE_FAILED,
+		             _("Couldn't initialize PKCS#12 decoder: %s"),
+		             gnutls_strerror (err));
+		return FALSE;
+	}
+
+	/* DER first */
+	err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0);
+	if (err < 0) {
+		/* PEM next */
+		err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0);
+		if (err < 0) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+			             _("Couldn't decode PKCS#12 file: %s"),
+			             gnutls_strerror (err));
+			goto out;
+		}
+	}
+
+	err = gnutls_pkcs12_verify_mac (p12, password);
+	if (err == GNUTLS_E_SUCCESS)
+		success = TRUE;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Couldn't verify PKCS#12 file: %s"),
+		             gnutls_strerror (err));
+	}
+
+out:
+	gnutls_pkcs12_deinit (p12);
+	return success;
+}
+
+gboolean
+crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+{
+	gcry_randomize (buffer, buffer_len, GCRY_STRONG_RANDOM);
+	return TRUE;
+}
+
diff --git a/libnm-util/crypto_nss.c b/libnm-util/crypto_nss.c
new file mode 100644
index 00000000..490d5fe9
--- /dev/null
+++ b/libnm-util/crypto_nss.c
@@ -0,0 +1,562 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <prinit.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <pkcs11t.h>
+#include <cert.h>
+#include <prerror.h>
+#include <p12.h>
+#include <ciferfam.h>
+#include <p12plcy.h>
+
+#include "crypto.h"
+
+static gboolean initialized = FALSE;
+
+gboolean
+crypto_init (GError **error)
+{
+	SECStatus ret;
+
+	if (initialized)
+		return TRUE;
+
+	PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
+	ret = NSS_NoDB_Init (NULL);
+	if (ret != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_INIT_FAILED,
+		             _("Failed to initialize the crypto engine: %d."),
+		             PR_GetError ());
+		PR_Cleanup ();
+		return FALSE;
+	}
+
+	SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
+	SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
+	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
+	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
+	SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
+	SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
+	SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
+
+	initialized = TRUE;
+	return TRUE;
+}
+
+void
+crypto_deinit (void)
+{
+	if (initialized) {
+		NSS_Shutdown ();
+		PR_Cleanup ();
+	}
+}
+
+gboolean
+crypto_md5_hash (const char *salt,
+                 const gsize salt_len,
+                 const char *password,
+                 gsize password_len,
+                 char *buffer,
+                 gsize buflen,
+                 GError **error)
+{
+	PK11Context *ctx;
+	int nkey = buflen;
+	unsigned int digest_len;
+	int count = 0;
+	char digest[MD5_HASH_LEN];
+	char *p = buffer;
+
+	if (salt)
+		g_return_val_if_fail (salt_len >= 8, FALSE);
+
+	g_return_val_if_fail (password != NULL, FALSE);
+	g_return_val_if_fail (password_len > 0, FALSE);
+	g_return_val_if_fail (buffer != NULL, FALSE);
+	g_return_val_if_fail (buflen > 0, FALSE);
+
+	ctx = PK11_CreateDigestContext (SEC_OID_MD5);
+	if (!ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_MD5_INIT_FAILED,
+		             _("Failed to initialize the MD5 context: %d."),
+		             PORT_GetError ());
+		return FALSE;
+	}
+
+	while (nkey > 0) {
+		int i = 0;
+
+		PK11_DigestBegin (ctx);
+		if (count++)
+			PK11_DigestOp (ctx, (const unsigned char *) digest, digest_len);
+		PK11_DigestOp (ctx, (const unsigned char *) password, password_len);
+		if (salt)
+			PK11_DigestOp (ctx, (const unsigned char *) salt, 8); /* Only use 8 bytes of salt */
+		PK11_DigestFinal (ctx, (unsigned char *) digest, &digest_len, sizeof (digest));
+
+		while (nkey && (i < digest_len)) {
+			*(p++) = digest[i++];
+			nkey--;
+		}
+	}
+
+	memset (digest, 0, sizeof (digest));
+	PK11_DestroyContext (ctx, PR_TRUE);
+	return TRUE;
+}
+
+char *
+crypto_decrypt (const char *cipher,
+                int key_type,
+                GByteArray *data,
+                const char *iv,
+                const gsize iv_len,
+                const char *key,
+                const gsize key_len,
+                gsize *out_len,
+                GError **error)
+{
+	char *output = NULL;
+	int decrypted_len = 0;
+	CK_MECHANISM_TYPE cipher_mech;
+	PK11SlotInfo *slot = NULL;
+	SECItem key_item;
+	PK11SymKey *sym_key = NULL;
+	SECItem *sec_param = NULL;
+	PK11Context *ctx = NULL;
+	SECStatus s;
+	gboolean success = FALSE;
+	unsigned int pad_len = 0, extra = 0;
+	guint32 i, real_iv_len = 0;
+
+	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
+		cipher_mech = CKM_DES3_CBC_PAD;
+		real_iv_len = 8;
+	} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
+		cipher_mech = CKM_DES_CBC_PAD;
+		real_iv_len = 8;
+	} else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	if (iv_len < real_iv_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_RAW_IV_INVALID,
+		             _("Invalid IV length (must be at least %d)."),
+		             real_iv_len);
+		return NULL;
+	}
+
+	output = g_malloc0 (data->len);
+	if (!output) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory for decrypted key buffer."));
+		return NULL;
+	}
+
+	slot = PK11_GetBestSlot (cipher_mech, NULL);
+	if (!slot) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the decryption cipher slot."));
+		goto out;
+	}
+
+	key_item.data = (unsigned char *) key;
+	key_item.len = key_len;
+	sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL);
+	if (!sym_key) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+		             _("Failed to set symmetric key for decryption."));
+		goto out;
+	}
+
+	key_item.data = (unsigned char *) iv;
+	key_item.len = real_iv_len;
+	sec_param = PK11_ParamFromIV (cipher_mech, &key_item);
+	if (!sec_param) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+		             _("Failed to set IV for decryption."));
+		goto out;
+	}
+
+	ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_DECRYPT, sym_key, sec_param);
+	if (!ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the decryption context."));
+		goto out;
+	}
+
+	s = PK11_CipherOp (ctx,
+	                   (unsigned char *) output,
+	                   &decrypted_len,
+	                   data->len,
+	                   data->data,
+	                   data->len);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: %d."),
+		             PORT_GetError ());
+		goto out;
+	}
+
+	if (decrypted_len > data->len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: decrypted data too large."));
+		goto out;
+	}
+
+	s = PK11_DigestFinal (ctx,
+	                      (unsigned char *) (output + decrypted_len),
+	                      &extra,
+	                      data->len - decrypted_len);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to finalize decryption of the private key: %d."),
+		             PORT_GetError ());
+		goto out;
+	}
+	decrypted_len += extra;
+	pad_len = data->len - decrypted_len;
+
+	/* Check if the padding at the end of the decrypted data is valid */
+	if (pad_len == 0 || pad_len > real_iv_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Failed to decrypt the private key: unexpected padding length."));
+		goto out;
+	}
+
+	/* Validate tail padding; last byte is the padding size, and all pad bytes
+	 * should contain the padding size.
+	 */
+	for (i = pad_len; i > 0; i--) {
+		if (output[data->len - i] != pad_len) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+			             _("Failed to decrypt the private key."));
+			goto out;
+		}
+	}
+
+	*out_len = decrypted_len;
+	success = TRUE;
+
+out:
+	if (ctx)
+		PK11_DestroyContext (ctx, PR_TRUE);
+	if (sym_key)
+		PK11_FreeSymKey (sym_key);
+	if (sec_param)
+		SECITEM_FreeItem (sec_param, PR_TRUE);
+	if (slot)
+		PK11_FreeSlot (slot);
+
+	if (!success) {
+		if (output) {
+			/* Don't expose key material */
+			memset (output, 0, data->len);
+			g_free (output);
+			output = NULL;
+		}
+	}
+	return output;
+}
+
+char *
+crypto_encrypt (const char *cipher,
+                const GByteArray *data,
+                const char *iv,
+                gsize iv_len,
+                const char *key,
+                gsize key_len,
+                gsize *out_len,
+                GError **error)
+{
+	SECStatus ret;
+	CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD;
+	PK11SlotInfo *slot = NULL;
+	SECItem key_item = { .data = (unsigned char *) key, .len = key_len };
+	SECItem iv_item = { .data = (unsigned char *) iv, .len = iv_len };
+	PK11SymKey *sym_key = NULL;
+	SECItem *sec_param = NULL;
+	PK11Context *ctx = NULL;
+	unsigned char *output, *padded_buf;
+	gsize output_len;
+	int encrypted_len = 0, i;
+	gboolean success = FALSE;
+	gsize padded_buf_len, pad_len;
+
+	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
+		cipher_mech = CKM_DES3_CBC_PAD;
+	else {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+		             _("Private key cipher '%s' was unknown."),
+		             cipher);
+		return NULL;
+	}
+
+	/* If data->len % ivlen == 0, then we add another complete block
+	 * onto the end so that the decrypter knows there's padding.
+	 */
+	pad_len = iv_len - (data->len % iv_len);
+	output_len = padded_buf_len = data->len + pad_len;
+	padded_buf = g_malloc0 (padded_buf_len);
+
+	memcpy (padded_buf, data->data, data->len);
+	for (i = 0; i < pad_len; i++)
+		padded_buf[data->len + i] = (guint8) (pad_len & 0xFF);
+
+	output = g_malloc0 (output_len);
+	if (!output) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Could not allocate memory for encrypting."));
+		return NULL;
+	}
+
+	slot = PK11_GetBestSlot (cipher_mech, NULL);
+	if (!slot) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the encryption cipher slot."));
+		goto out;
+	}
+
+	sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL);
+	if (!sym_key) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+		             _("Failed to set symmetric key for encryption."));
+		goto out;
+	}
+
+	sec_param = PK11_ParamFromIV (cipher_mech, &iv_item);
+	if (!sec_param) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+		             _("Failed to set IV for encryption."));
+		goto out;
+	}
+
+	ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_ENCRYPT, sym_key, sec_param);
+	if (!ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+		             _("Failed to initialize the encryption context."));
+		goto out;
+	}
+
+	ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len);
+	if (ret != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED,
+		             _("Failed to encrypt: %d."),
+		             PORT_GetError ());
+		goto out;
+	}
+
+	if (encrypted_len != output_len) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED,
+		             _("Unexpected amount of data after encrypting."));
+		goto out;
+	}
+
+	*out_len = encrypted_len;
+	success = TRUE;
+
+out:
+	if (ctx)
+		PK11_DestroyContext (ctx, PR_TRUE);
+	if (sym_key)
+		PK11_FreeSymKey (sym_key);
+	if (sec_param)
+		SECITEM_FreeItem (sec_param, PR_TRUE);
+	if (slot)
+		PK11_FreeSlot (slot);
+
+	if (padded_buf) {
+		memset (padded_buf, 0, padded_buf_len);
+		g_free (padded_buf);
+		padded_buf = NULL;
+	}
+
+	if (!success) {
+		memset (output, 0, output_len);
+		g_free (output);
+		output = NULL;
+	}
+	return (char *) output;
+}
+
+NMCryptoFileFormat
+crypto_verify_cert (const unsigned char *data,
+                    gsize len,
+                    GError **error)
+{
+	CERTCertificate *cert;
+
+	/* Try DER/PEM first */
+	cert = CERT_DecodeCertFromPackage ((char *) data, len);
+	if (!cert) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+		             _("Couldn't decode certificate: %d"),
+		             PORT_GetError());
+		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	}
+
+	CERT_DestroyCertificate (cert);
+	return NM_CRYPTO_FILE_FORMAT_X509;
+}
+
+gboolean
+crypto_verify_pkcs12 (const GByteArray *data,
+                      const char *password,
+                      GError **error)
+{
+	SEC_PKCS12DecoderContext *p12ctx = NULL;
+	SECItem pw = { 0 };
+	PK11SlotInfo *slot = NULL;
+	SECStatus s;
+	char *ucs2_password;
+	glong ucs2_chars = 0;
+#ifndef WORDS_BIGENDIAN
+	guint16 *p;
+#endif /* WORDS_BIGENDIAN */
+
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	/* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do
+	 * any conversions for us.
+	 */
+	if (password && strlen (password)) {
+		ucs2_password = (char *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
+		if (!ucs2_password || !ucs2_chars) {
+			g_set_error (error, NM_CRYPTO_ERROR,
+			             NM_CRYPTO_ERR_INVALID_PASSWORD,
+			             _("Couldn't convert password to UCS2: %d"),
+			             PORT_GetError());
+			return FALSE;
+		}
+
+		ucs2_chars *= 2;  /* convert # UCS2 characters -> bytes */
+		pw.data = PORT_ZAlloc(ucs2_chars + 2);
+		memcpy (pw.data, ucs2_password, ucs2_chars);
+		pw.len = ucs2_chars + 2;  /* include terminating NULL */
+
+		memset (ucs2_password, 0, ucs2_chars);
+		g_free (ucs2_password);
+
+#ifndef WORDS_BIGENDIAN
+		for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
+			*p = GUINT16_SWAP_LE_BE (*p);
+#endif /* WORDS_BIGENDIAN */
+	} else {
+		/* NULL password */
+		pw.data = NULL;
+		pw.len = 0;
+	}
+
+	slot = PK11_GetInternalKeySlot();
+	p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL);
+	if (!p12ctx) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_DECODE_FAILED,
+		             _("Couldn't initialize PKCS#12 decoder: %d"),
+		             PORT_GetError());
+		goto error;
+	}
+
+	s = SEC_PKCS12DecoderUpdate (p12ctx, data->data, data->len);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+		             _("Couldn't decode PKCS#12 file: %d"),
+		             PORT_GetError());
+		goto error;
+	}
+
+	s = SEC_PKCS12DecoderVerify (p12ctx);
+	if (s != SECSuccess) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+		             _("Couldn't verify PKCS#12 file: %d"),
+		             PORT_GetError());
+		goto error;
+	}
+
+	SEC_PKCS12DecoderFinish (p12ctx);
+	SECITEM_ZfreeItem (&pw, PR_FALSE);
+	return TRUE;
+
+error:
+	if (p12ctx)
+		SEC_PKCS12DecoderFinish (p12ctx);
+
+	if (slot)
+		PK11_FreeSlot(slot);
+
+	SECITEM_ZfreeItem (&pw, PR_FALSE);
+	return FALSE;
+}
+
+gboolean
+crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+{
+	SECStatus s;
+
+	s = PK11_GenerateRandom (buffer, buffer_len);
+	if (s != SECSuccess) {
+		g_set_error_literal (error, NM_CRYPTO_ERROR,
+		                     NM_CRYPTO_ERR_RANDOMIZE_FAILED,
+		                     _("Could not generate random data."));
+		return FALSE;
+	}
+	return TRUE;
+}
+
diff --git a/libnm-util/libnm-util.pc.in b/libnm-util/libnm-util.pc.in
new file mode 100644
index 00000000..a6e8ee59
--- /dev/null
+++ b/libnm-util/libnm-util.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnm-util
+Description: Convenience library for clients of NetworkManager
+Version: @VERSION@
+Requires: glib-2.0 dbus-glib-1
+Cflags: -I${includedir}/NetworkManager
+Libs: -L${libdir} -lnm-util
+
+
diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver
new file mode 100644
index 00000000..ed1d8a48
--- /dev/null
+++ b/libnm-util/libnm-util.ver
@@ -0,0 +1,394 @@
+{
+global:
+	nm_connection_add_setting;
+	nm_connection_clear_secrets;
+	nm_connection_compare;
+	nm_connection_create_setting;
+	nm_connection_dump;
+	nm_connection_duplicate;
+	nm_connection_error_get_type;
+	nm_connection_error_quark;
+	nm_connection_for_each_setting_value;
+	nm_connection_get_path;
+	nm_connection_get_scope;
+	nm_connection_get_setting;
+	nm_connection_get_setting_by_name;
+	nm_connection_get_type;
+	nm_connection_lookup_setting_type;
+	nm_connection_lookup_setting_type_by_quark;
+	nm_connection_need_secrets;
+	nm_connection_new;
+	nm_connection_new_from_hash;
+	nm_connection_remove_setting;
+	nm_connection_replace_settings;
+	nm_connection_set_path;
+	nm_connection_set_scope;
+	nm_connection_to_hash;
+	nm_connection_update_secrets;
+	nm_connection_verify;
+	nm_setting_802_1x_add_eap_method;
+	nm_setting_802_1x_clear_eap_methods;
+	nm_setting_802_1x_error_get_type;
+	nm_setting_802_1x_error_quark;
+	nm_setting_802_1x_get_anonymous_identity;
+	nm_setting_802_1x_get_ca_cert;
+	nm_setting_802_1x_get_ca_cert_blob;
+	nm_setting_802_1x_set_ca_cert_from_file;
+	nm_setting_802_1x_get_ca_cert_path;
+	nm_setting_802_1x_get_ca_cert_scheme;
+	nm_setting_802_1x_set_ca_cert;
+	nm_setting_802_1x_get_ca_path;
+	nm_setting_802_1x_get_client_cert;
+	nm_setting_802_1x_get_client_cert_blob;
+	nm_setting_802_1x_set_client_cert_from_file;
+	nm_setting_802_1x_get_client_cert_path;
+	nm_setting_802_1x_get_client_cert_scheme;
+	nm_setting_802_1x_set_client_cert;
+	nm_setting_802_1x_get_eap_method;
+	nm_setting_802_1x_get_identity;
+	nm_setting_802_1x_get_num_eap_methods;
+	nm_setting_802_1x_get_password;
+	nm_setting_802_1x_get_phase1_fast_provisioning;
+	nm_setting_802_1x_get_phase1_peaplabel;
+	nm_setting_802_1x_get_phase1_peapver;
+	nm_setting_802_1x_get_phase2_auth;
+	nm_setting_802_1x_get_phase2_autheap;
+	nm_setting_802_1x_get_phase2_ca_cert;
+	nm_setting_802_1x_get_phase2_ca_cert_blob;
+	nm_setting_802_1x_set_phase2_ca_cert_from_file;
+	nm_setting_802_1x_get_phase2_ca_cert_path;
+	nm_setting_802_1x_get_phase2_ca_cert_scheme;
+	nm_setting_802_1x_set_phase2_ca_cert;
+	nm_setting_802_1x_get_phase2_ca_path;
+	nm_setting_802_1x_get_phase2_client_cert;
+	nm_setting_802_1x_get_phase2_client_cert_blob;
+	nm_setting_802_1x_set_phase2_client_cert_from_file;
+	nm_setting_802_1x_get_phase2_client_cert_path;
+	nm_setting_802_1x_get_phase2_client_cert_scheme;
+	nm_setting_802_1x_set_phase2_client_cert;
+	nm_setting_802_1x_get_phase2_private_key;
+	nm_setting_802_1x_get_phase2_private_key_blob;
+	nm_setting_802_1x_get_phase2_private_key_format;
+	nm_setting_802_1x_set_phase2_private_key_from_file;
+	nm_setting_802_1x_get_phase2_private_key_password;
+	nm_setting_802_1x_get_phase2_private_key_path;
+	nm_setting_802_1x_get_phase2_private_key_scheme;
+	nm_setting_802_1x_get_phase2_private_key_type;
+	nm_setting_802_1x_set_phase2_private_key;
+	nm_setting_802_1x_get_pin;
+	nm_setting_802_1x_get_private_key;
+	nm_setting_802_1x_get_private_key_blob;
+	nm_setting_802_1x_get_private_key_format;
+	nm_setting_802_1x_set_private_key_from_file;
+	nm_setting_802_1x_get_private_key_password;
+	nm_setting_802_1x_get_private_key_path;
+	nm_setting_802_1x_get_private_key_scheme;
+	nm_setting_802_1x_get_private_key_type;
+	nm_setting_802_1x_set_private_key;
+	nm_setting_802_1x_get_psk;
+	nm_setting_802_1x_get_system_ca_certs;
+	nm_setting_802_1x_get_type;
+	nm_setting_802_1x_new;
+	nm_setting_802_1x_remove_eap_method;
+	nm_setting_bluetooth_error_get_type;
+	nm_setting_bluetooth_error_quark;
+	nm_setting_bluetooth_get_type;
+	nm_setting_bluetooth_new;
+	nm_setting_bluetooth_get_bdaddr;
+	nm_setting_bluetooth_get_connection_type;
+	nm_setting_cdma_error_get_type;
+	nm_setting_cdma_error_quark;
+	nm_setting_cdma_get_type;
+	nm_setting_cdma_new;
+	nm_setting_cdma_get_number;
+	nm_setting_cdma_get_username;
+	nm_setting_cdma_get_password;
+	nm_setting_clear_secrets;
+	nm_setting_compare;
+	nm_setting_connection_error_get_type;
+	nm_setting_connection_error_quark;
+	nm_setting_connection_get_type;
+	nm_setting_connection_new;
+	nm_setting_connection_get_id;
+	nm_setting_connection_get_uuid;
+	nm_setting_connection_get_connection_type;
+	nm_setting_connection_get_autoconnect;
+	nm_setting_connection_get_timestamp;
+	nm_setting_connection_get_read_only;
+	nm_setting_duplicate;
+	nm_setting_enumerate_values;
+	nm_setting_error_get_type;
+	nm_setting_error_quark;
+	nm_setting_new_from_hash;
+	nm_setting_get_name;
+	nm_setting_get_type;
+	nm_setting_gsm_error_get_type;
+	nm_setting_gsm_error_quark;
+	nm_setting_gsm_get_type;
+	nm_setting_gsm_new;
+	nm_setting_gsm_get_number;
+	nm_setting_gsm_get_username;
+	nm_setting_gsm_get_password;
+	nm_setting_gsm_get_apn;
+	nm_setting_gsm_get_network_id;
+	nm_setting_gsm_get_network_type;
+	nm_setting_gsm_get_allowed_bands;
+	nm_setting_gsm_get_band;
+	nm_setting_gsm_get_pin;
+	nm_setting_gsm_get_puk;
+	nm_setting_gsm_get_home_only;
+	nm_setting_ip4_config_error_get_type;
+	nm_setting_ip4_config_error_quark;
+	nm_setting_ip4_config_get_type;
+	nm_setting_ip4_config_new;
+	nm_setting_ip4_config_get_method;
+	nm_setting_ip4_config_get_num_dns;
+	nm_setting_ip4_config_get_dns;
+	nm_setting_ip4_config_add_dns;
+	nm_setting_ip4_config_remove_dns;
+	nm_setting_ip4_config_clear_dns;
+	nm_setting_ip4_config_get_num_dns_searches;
+	nm_setting_ip4_config_get_dns_search;
+	nm_setting_ip4_config_add_dns_search;
+	nm_setting_ip4_config_remove_dns_search;
+	nm_setting_ip4_config_clear_dns_searches;
+	nm_setting_ip4_config_get_num_addresses;
+	nm_setting_ip4_config_get_address;
+	nm_setting_ip4_config_add_address;
+	nm_setting_ip4_config_remove_address;
+	nm_setting_ip4_config_clear_addresses;
+	nm_setting_ip4_config_get_num_routes;
+	nm_setting_ip4_config_get_route;
+	nm_setting_ip4_config_add_route;
+	nm_setting_ip4_config_remove_route;
+	nm_setting_ip4_config_clear_routes;
+	nm_setting_ip4_config_get_ignore_auto_routes;
+	nm_setting_ip4_config_get_ignore_auto_dns;
+	nm_setting_ip4_config_get_dhcp_client_id;
+	nm_setting_ip4_config_get_dhcp_hostname;
+	nm_setting_ip4_config_get_dhcp_send_hostname;
+	nm_setting_ip4_config_get_never_default;
+	nm_setting_ip4_config_get_may_fail;
+	nm_ip6_address_new;
+	nm_ip6_address_dup;
+	nm_ip6_address_ref;
+	nm_ip6_address_unref;
+	nm_ip6_address_compare;
+	nm_ip6_address_get_address;
+	nm_ip6_address_set_address;
+	nm_ip6_address_get_gateway;
+	nm_ip6_address_set_gateway;
+	nm_ip6_address_get_prefix;
+	nm_ip6_address_set_prefix;
+	nm_ip6_route_new;
+	nm_ip6_route_dup;
+	nm_ip6_route_ref;
+	nm_ip6_route_unref;
+	nm_ip6_route_compare;
+	nm_ip6_route_get_dest;
+	nm_ip6_route_set_dest;
+	nm_ip6_route_get_prefix;
+	nm_ip6_route_set_prefix;
+	nm_ip6_route_get_next_hop;
+	nm_ip6_route_set_next_hop;
+	nm_ip6_route_get_metric;
+	nm_ip6_route_set_metric;
+	nm_setting_ip6_config_error_get_type;
+	nm_setting_ip6_config_error_quark;
+	nm_setting_ip6_config_get_type;
+	nm_setting_ip6_config_new;
+	nm_setting_ip6_config_get_method;
+	nm_setting_ip6_config_get_num_dns;
+	nm_setting_ip6_config_get_dns;
+	nm_setting_ip6_config_add_dns;
+	nm_setting_ip6_config_remove_dns;
+	nm_setting_ip6_config_clear_dns;
+	nm_setting_ip6_config_get_num_dns_searches;
+	nm_setting_ip6_config_get_dns_search;
+	nm_setting_ip6_config_add_dns_search;
+	nm_setting_ip6_config_remove_dns_search;
+	nm_setting_ip6_config_clear_dns_searches;
+	nm_setting_ip6_config_get_num_addresses;
+	nm_setting_ip6_config_get_address;
+	nm_setting_ip6_config_add_address;
+	nm_setting_ip6_config_remove_address;
+	nm_setting_ip6_config_clear_addresses;
+	nm_setting_ip6_config_get_num_routes;
+	nm_setting_ip6_config_get_route;
+	nm_setting_ip6_config_add_route;
+	nm_setting_ip6_config_remove_route;
+	nm_setting_ip6_config_clear_routes;
+	nm_setting_ip6_config_get_ignore_auto_routes;
+	nm_setting_ip6_config_get_ignore_auto_dns;
+	nm_setting_ip6_config_get_never_default;
+	nm_setting_ip6_config_get_may_fail;
+	nm_setting_need_secrets;
+	nm_setting_ppp_error_get_type;
+	nm_setting_ppp_error_quark;
+	nm_setting_ppp_get_type;
+	nm_setting_ppp_new;
+	nm_setting_ppp_get_noauth;
+	nm_setting_ppp_get_refuse_eap;
+	nm_setting_ppp_get_refuse_pap;
+	nm_setting_ppp_get_refuse_chap;
+	nm_setting_ppp_get_refuse_mschap;
+	nm_setting_ppp_get_refuse_mschapv2;
+	nm_setting_ppp_get_nobsdcomp;
+	nm_setting_ppp_get_nodeflate;
+	nm_setting_ppp_get_no_vj_comp;
+	nm_setting_ppp_get_require_mppe;
+	nm_setting_ppp_get_require_mppe_128;
+	nm_setting_ppp_get_mppe_stateful;
+	nm_setting_ppp_get_crtscts;
+	nm_setting_ppp_get_baud;
+	nm_setting_ppp_get_mru;
+	nm_setting_ppp_get_mtu;
+	nm_setting_ppp_get_lcp_echo_failure;
+	nm_setting_ppp_get_lcp_echo_interval;
+	nm_setting_pppoe_error_get_type;
+	nm_setting_pppoe_error_quark;
+	nm_setting_pppoe_get_type;
+	nm_setting_pppoe_new;
+	nm_setting_pppoe_get_service;
+	nm_setting_pppoe_get_username;
+	nm_setting_pppoe_get_password;
+	nm_setting_serial_error_get_type;
+	nm_setting_serial_error_quark;
+	nm_setting_serial_get_type;
+	nm_setting_serial_new;
+	nm_setting_serial_get_baud;
+	nm_setting_serial_get_bits;
+	nm_setting_serial_get_parity;
+	nm_setting_serial_get_stopbits;
+	nm_setting_serial_get_send_delay;
+	nm_setting_to_hash;
+	nm_setting_to_string;
+	nm_setting_update_secrets;
+	nm_setting_verify;
+	nm_setting_vpn_error_get_type;
+	nm_setting_vpn_error_quark;
+	nm_setting_vpn_get_type;
+	nm_setting_vpn_new;
+	nm_setting_vpn_get_service_type;
+	nm_setting_vpn_get_user_name;
+	nm_setting_vpn_add_data_item;
+	nm_setting_vpn_foreach_data_item;
+	nm_setting_vpn_get_data_item;
+	nm_setting_vpn_remove_data_item;
+	nm_setting_vpn_add_secret;
+	nm_setting_vpn_foreach_secret;
+	nm_setting_vpn_get_secret;
+	nm_setting_vpn_remove_secret;
+	nm_setting_wired_error_get_type;
+	nm_setting_wired_error_quark;
+	nm_setting_wired_get_type;
+	nm_setting_wired_new;
+	nm_setting_wired_get_port;
+	nm_setting_wired_get_speed;
+	nm_setting_wired_get_duplex;
+	nm_setting_wired_get_auto_negotiate;
+	nm_setting_wired_get_mac_address;
+	nm_setting_wired_get_mtu;
+	nm_setting_wireless_ap_security_compatible;
+	nm_setting_wireless_error_get_type;
+	nm_setting_wireless_error_quark;
+	nm_setting_wireless_get_type;
+	nm_setting_wireless_new;
+	nm_setting_wireless_get_ssid;
+	nm_setting_wireless_get_mode;
+	nm_setting_wireless_get_band;
+	nm_setting_wireless_get_channel;
+	nm_setting_wireless_get_bssid;
+	nm_setting_wireless_get_rate;
+	nm_setting_wireless_get_tx_power;
+	nm_setting_wireless_get_mac_address;
+	nm_setting_wireless_get_mtu;
+	nm_setting_wireless_get_security;
+	nm_setting_wireless_add_seen_bssid;
+	nm_setting_wireless_get_num_seen_bssids;
+	nm_setting_wireless_get_seen_bssid;
+	nm_setting_wireless_security_add_proto;
+	nm_setting_wireless_security_add_pairwise;
+	nm_setting_wireless_security_add_group;
+	nm_setting_wireless_security_clear_groups;
+	nm_setting_wireless_security_clear_pairwise;
+	nm_setting_wireless_security_clear_protos;
+	nm_setting_wireless_security_error_get_type;
+	nm_setting_wireless_security_error_quark;
+	nm_setting_wireless_security_get_auth_alg;
+	nm_setting_wireless_security_get_group;
+	nm_setting_wireless_security_get_key_mgmt;
+	nm_setting_wireless_security_get_leap_username;
+	nm_setting_wireless_security_get_leap_password;
+	nm_setting_wireless_security_get_num_groups;
+	nm_setting_wireless_security_get_num_pairwise;
+	nm_setting_wireless_security_get_num_protos;
+	nm_setting_wireless_security_get_pairwise;
+	nm_setting_wireless_security_get_proto;
+	nm_setting_wireless_security_get_psk;
+	nm_setting_wireless_security_get_type;
+	nm_setting_wireless_security_get_wep_key;
+	nm_setting_wireless_security_get_wep_key_type;
+	nm_setting_wireless_security_get_wep_tx_keyidx;
+	nm_setting_wireless_security_new;
+	nm_setting_wireless_security_remove_group;
+	nm_setting_wireless_security_remove_pairwise;
+	nm_setting_wireless_security_remove_proto;
+	nm_setting_wireless_security_set_wep_key;
+	nm_setting_olpc_mesh_error_quark;
+	nm_setting_olpc_mesh_error_get_type;
+	nm_setting_olpc_mesh_get_type;
+	nm_setting_olpc_mesh_get_ssid;
+	nm_setting_olpc_mesh_get_channel;
+	nm_setting_olpc_mesh_get_dhcp_anycast_address;
+	nm_setting_olpc_mesh_new;
+	nm_utils_deinit;
+	nm_utils_rsa_key_encrypt;
+	nm_utils_escape_ssid;
+	nm_utils_gvalue_hash_dup;
+	nm_utils_init;
+	nm_utils_ip4_addresses_from_gvalue;
+	nm_utils_ip4_addresses_to_gvalue;
+	nm_utils_ip4_netmask_to_prefix;
+	nm_utils_ip4_prefix_to_netmask;
+	nm_utils_ip4_get_default_prefix;
+	nm_utils_ip4_routes_from_gvalue;
+	nm_utils_ip4_routes_to_gvalue;
+	nm_utils_ip6_addresses_from_gvalue;
+	nm_utils_ip6_addresses_to_gvalue;
+	nm_utils_ip6_routes_from_gvalue;
+	nm_utils_ip6_routes_to_gvalue;
+	nm_utils_is_empty_ssid;
+	nm_utils_same_ssid;
+	nm_utils_security_valid;
+	nm_utils_slist_free;
+	nm_utils_ssid_to_utf8;
+	nm_utils_uuid_generate;
+	nm_utils_uuid_generate_from_string;
+	nm_ip4_address_compare;
+	nm_ip4_address_dup;
+	nm_ip4_address_get_address;
+	nm_ip4_address_get_prefix;
+	nm_ip4_address_get_gateway;
+	nm_ip4_address_new;
+	nm_ip4_address_ref;
+	nm_ip4_address_set_address;
+	nm_ip4_address_set_prefix;
+	nm_ip4_address_set_gateway;
+	nm_ip4_address_unref;
+	nm_ip4_route_compare;
+	nm_ip4_route_dup;
+	nm_ip4_route_get_dest;
+	nm_ip4_route_get_prefix;
+	nm_ip4_route_get_next_hop;
+	nm_ip4_route_get_metric;
+	nm_ip4_route_new;
+	nm_ip4_route_ref;
+	nm_ip4_route_unref;
+	nm_ip4_route_set_dest;
+	nm_ip4_route_set_prefix;
+	nm_ip4_route_set_next_hop;
+	nm_ip4_route_set_metric;
+local:
+	*;
+};
diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c
new file mode 100644
index 00000000..d9cdc270
--- /dev/null
+++ b/libnm-util/nm-connection.c
@@ -0,0 +1,1210 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include "nm-connection.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+
+#include "nm-setting-8021x.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-olpc-mesh.h"
+
+#include "nm-setting-serial.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-cdma.h"
+
+/**
+ * SECTION:nm-connection
+ * @short_description: Describes a connection to specific network or provider
+ * @include: nm-connection.h
+ *
+ * An #NMConnection describes all the settings and configuration values that
+ * are necessary to configure network devices for operation on a specific
+ * network.  Connections are the fundamental operating object for
+ * NetworkManager; no device is connected without a #NMConnection, or
+ * disconnected without having been connected with a #NMConnection.
+ *
+ * Each #NMConnection contains a list of #NMSetting objects usually referenced
+ * by name (using nm_connection_get_setting_by_name()) or by type (with
+ * nm_connection_get_setting()).  The settings describe the actual parameters
+ * with which the network devices are configured, including device-specific
+ * parameters (MTU, SSID, APN, channel, rate, etc) and IP-level parameters
+ * (addresses, routes, addressing methods, etc).
+ *
+ * Most connections also have a %NMConnectionScope; a connection will be
+ * provided over D-Bus either by the user settings service
+ * (org.freedesktop.NetworkManagerUserSettings) running in an active user
+ * session, or by the system-wide system settings service
+ * (org.freedesktop.NetworkManagerSystemSettings) which provides  connections
+ * for all users.
+ */
+
+/**
+ * nm_connection_error_quark:
+ *
+ * Registers an error quark for #NMConnection if necessary.
+ *
+ * Returns: the error quark used for #NMConnection errors.
+ **/
+GQuark
+nm_connection_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-connection-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_connection_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (NM_CONNECTION_ERROR_UNKNOWN, "UnknownError"),
+			ENUM_ENTRY (NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND, "ConnectionSettingNotFound"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMConnectionError", values);
+	}
+	return etype;
+}
+
+typedef struct {
+	GHashTable *settings;
+
+	/* Type of the connection (system or user) */
+	NMConnectionScope scope;
+
+	/* D-Bus path of the connection, if any */
+	char *path;
+} NMConnectionPrivate;
+
+#define NM_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTION, NMConnectionPrivate))
+
+G_DEFINE_TYPE (NMConnection, nm_connection, G_TYPE_OBJECT)
+
+enum {
+	PROP_0,
+	PROP_SCOPE,
+	PROP_PATH,
+
+	LAST_PROP
+};
+
+enum {
+	SECRETS_UPDATED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GHashTable *registered_settings = NULL;
+
+#define DEFAULT_MAP_SIZE 15
+
+static struct SettingInfo {
+	const char *name;
+	GType type;
+	guint32 priority;
+	GQuark error_quark;
+} default_map[DEFAULT_MAP_SIZE] = { { NULL } };
+
+static void
+setting_register (const char *name, GType type)
+{
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (G_TYPE_IS_INSTANTIATABLE (type));
+
+	if (G_UNLIKELY (!registered_settings)) {
+		registered_settings = g_hash_table_new_full (g_str_hash, g_str_equal, 
+		                                             (GDestroyNotify) g_free,
+		                                             (GDestroyNotify) g_free);
+	}
+
+	if (g_hash_table_lookup (registered_settings, name))
+		g_warning ("Already have a creator function for '%s', overriding", name);
+
+	g_hash_table_insert (registered_settings, g_strdup (name), g_strdup (g_type_name (type)));
+}
+
+#if UNUSED
+static void
+setting_unregister (const char *name)
+{
+	if (registered_settings)
+		g_hash_table_remove (registered_settings, name);
+}
+#endif
+
+static void
+register_one_setting (const char *name, GType type, GQuark error_quark, guint32 priority)
+{
+	static guint32 i = 0;
+
+	g_return_if_fail (i < DEFAULT_MAP_SIZE);
+	g_return_if_fail (default_map[i].name == NULL);
+
+	default_map[i].name = name;
+	default_map[i].type = type;
+	default_map[i].error_quark = error_quark;
+	default_map[i].priority = priority;
+	i++;
+
+	setting_register (name, type);
+}
+
+static void
+register_default_settings (void)
+{
+	_nm_utils_register_value_transformations ();
+
+	if (G_LIKELY (default_map[0].name))
+		return;
+
+	register_one_setting (NM_SETTING_CONNECTION_SETTING_NAME,
+	                      NM_TYPE_SETTING_CONNECTION,
+	                      NM_SETTING_CONNECTION_ERROR,
+	                      0);
+
+	register_one_setting (NM_SETTING_WIRED_SETTING_NAME,
+	                      NM_TYPE_SETTING_WIRED,
+	                      NM_SETTING_WIRED_ERROR,
+	                      1);
+
+	register_one_setting (NM_SETTING_WIRELESS_SETTING_NAME,
+	                      NM_TYPE_SETTING_WIRELESS,
+	                      NM_SETTING_WIRELESS_ERROR,
+	                      1);
+
+	register_one_setting (NM_SETTING_OLPC_MESH_SETTING_NAME,
+	                      NM_TYPE_SETTING_OLPC_MESH,
+	                      NM_SETTING_OLPC_MESH_ERROR,
+	                      1);
+
+	register_one_setting (NM_SETTING_GSM_SETTING_NAME,
+	                      NM_TYPE_SETTING_GSM,
+	                      NM_SETTING_GSM_ERROR,
+	                      1);
+
+	register_one_setting (NM_SETTING_CDMA_SETTING_NAME,
+	                      NM_TYPE_SETTING_CDMA,
+	                      NM_SETTING_CDMA_ERROR,
+	                      1);
+
+	register_one_setting (NM_SETTING_BLUETOOTH_SETTING_NAME,
+			      NM_TYPE_SETTING_BLUETOOTH,
+			      NM_SETTING_BLUETOOTH_ERROR,
+			      1);
+
+	register_one_setting (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+	                      NM_TYPE_SETTING_WIRELESS_SECURITY,
+	                      NM_SETTING_WIRELESS_SECURITY_ERROR,
+	                      2);
+
+	register_one_setting (NM_SETTING_SERIAL_SETTING_NAME,
+	                      NM_TYPE_SETTING_SERIAL,
+	                      NM_SETTING_SERIAL_ERROR,
+	                      2);
+
+	register_one_setting (NM_SETTING_PPP_SETTING_NAME,
+	                      NM_TYPE_SETTING_PPP,
+	                      NM_SETTING_PPP_ERROR,
+	                      3);
+
+	register_one_setting (NM_SETTING_PPPOE_SETTING_NAME,
+	                      NM_TYPE_SETTING_PPPOE,
+	                      NM_SETTING_PPPOE_ERROR,
+	                      3);
+
+	register_one_setting (NM_SETTING_802_1X_SETTING_NAME,
+	                      NM_TYPE_SETTING_802_1X,
+	                      NM_SETTING_802_1X_ERROR,
+	                      3);
+
+	register_one_setting (NM_SETTING_VPN_SETTING_NAME,
+	                      NM_TYPE_SETTING_VPN,
+	                      NM_SETTING_VPN_ERROR,
+	                      4);
+
+	register_one_setting (NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	                      NM_TYPE_SETTING_IP4_CONFIG,
+	                      NM_SETTING_IP4_CONFIG_ERROR,
+	                      6);
+
+	register_one_setting (NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	                      NM_TYPE_SETTING_IP6_CONFIG,
+	                      NM_SETTING_IP6_CONFIG_ERROR,
+	                      6);
+
+	/* Be sure to update DEFAULT_MAP_SIZE if you add another setting!! */
+}
+
+static guint32
+get_priority_for_setting_type (GType type)
+{
+	int i;
+
+	for (i = 0; default_map[i].name; i++) {
+		if (default_map[i].type == type)
+			return default_map[i].priority;
+	}
+
+	return G_MAXUINT32;
+}
+
+/**
+ * nm_connection_lookup_setting_type:
+ * @name: a setting name
+ *
+ * Returns the #GType of the setting's class for a given setting name.
+ *
+ * Returns: the #GType of the setting's class
+ **/
+GType
+nm_connection_lookup_setting_type (const char *name)
+{
+	char *type_name;
+	GType type;
+
+	g_return_val_if_fail (name != NULL, G_TYPE_NONE);
+
+	if (!registered_settings)
+		register_default_settings ();
+
+	type_name = (char *) g_hash_table_lookup (registered_settings, name);
+	if (type_name) {
+		type = g_type_from_name (type_name);
+		if (!type)
+			g_warning ("Can not get type for '%s'.", type_name);
+	} else {
+		type = 0;
+		g_warning ("Unknown setting '%s'", name);
+	}
+
+	return type;
+}
+
+/**
+ * nm_connection_lookup_setting_type_by_quark:
+ * @error_quark: a setting error quark
+ *
+ * Returns the #GType of the setting's class for a given setting error quark.
+ * Useful for figuring out which setting a returned error is for.
+ *
+ * Returns: the #GType of the setting's class
+ **/
+GType
+nm_connection_lookup_setting_type_by_quark (GQuark error_quark)
+{
+	int i;
+
+	for (i = 0; default_map[i].name; i++) {
+		if (default_map[i].error_quark == error_quark)
+			return default_map[i].type;
+	}
+
+	return G_TYPE_INVALID;
+}
+
+/**
+ * nm_connection_create_setting:
+ * @name: a setting name
+ *
+ * Create a new #NMSetting object of the desired type, given a setting name.
+ *
+ * Returns: the new setting object, or NULL if the setting name was unknown
+ **/
+NMSetting *
+nm_connection_create_setting (const char *name)
+{
+	GType type;
+	NMSetting *setting = NULL;
+
+	g_return_val_if_fail (name != NULL, NULL);
+
+	type = nm_connection_lookup_setting_type (name);
+	if (type)
+		setting = (NMSetting *) g_object_new (type, NULL);
+
+	return setting;
+}
+
+static void
+parse_one_setting (gpointer key, gpointer value, gpointer user_data)
+{
+	NMConnection *connection = (NMConnection *) user_data;
+	GType type;
+	NMSetting *setting = NULL;
+
+	type = nm_connection_lookup_setting_type ((char *) key);
+	if (type)
+		setting = nm_setting_new_from_hash (type, (GHashTable *) value);
+	if (setting)
+		nm_connection_add_setting (connection, setting);
+}
+
+/**
+ * nm_connection_add_setting:
+ * @connection: a #NMConnection
+ * @setting: the #NMSetting to add to the connection object
+ *
+ * Adds a #NMSetting to the connection, replacing any previous #NMSetting of the
+ * same name which has previously been added to the #NMConnection.  The
+ * connection takes ownership of the #NMSetting object and does not increase
+ * the setting object's reference count.
+ **/
+void
+nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
+{
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+	g_return_if_fail (NM_IS_SETTING (setting));
+
+	g_hash_table_insert (NM_CONNECTION_GET_PRIVATE (connection)->settings,
+					 g_strdup (G_OBJECT_TYPE_NAME (setting)), setting);
+}
+
+/**
+ * nm_connection_remove_setting:
+ * @connection: a #NMConnection
+ * @setting_type: the #GType of the setting object to remove
+ *
+ * Removes the #NMSetting with the given #GType from the #NMConnection.  This
+ * operation dereferences the #NMSetting object.
+ **/
+void
+nm_connection_remove_setting (NMConnection *connection, GType setting_type)
+{
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+	g_return_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING));
+
+	g_hash_table_remove (NM_CONNECTION_GET_PRIVATE (connection)->settings, g_type_name (setting_type));
+}
+
+/**
+ * nm_connection_get_setting:
+ * @connection: a #NMConnection
+ * @setting_type: the #GType of the setting object to return
+ *
+ * Gets the #NMSetting with the given #GType, if one has been previously added
+ * to the #NMConnection.
+ *
+ * Returns: the #NMSetting, or NULL if no setting of that type was previously
+ * added to the #NMConnection
+ **/
+NMSetting *
+nm_connection_get_setting (NMConnection *connection, GType setting_type)
+{
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), NULL);
+
+	return (NMSetting *) g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
+									  g_type_name (setting_type));
+}
+
+/**
+ * nm_connection_get_setting_by_name:
+ * @connection: a #NMConnection
+ * @name: a setting name
+ *
+ * Gets the #NMSetting with the given name, if one has been previously added
+ * the the #NMConnection.
+ *
+ * Returns: the #NMSetting, or NULL if no setting with that name was previously
+ * added to the #NMConnection
+ **/
+NMSetting *
+nm_connection_get_setting_by_name (NMConnection *connection, const char *name)
+{
+	GType type;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+
+	type = nm_connection_lookup_setting_type (name);
+
+	return type ? nm_connection_get_setting (connection, type) : NULL;
+}
+
+/**
+ * nm_connection_replace_settings:
+ * @connection: a #NMConnection
+ * @new_settings: a #GHashTable of settings
+ * @error: location to store error, or %NULL
+ *
+ * Returns: %TRUE if the settings were valid and added to the connection, %FALSE
+ * if they were not
+ **/
+gboolean
+nm_connection_replace_settings (NMConnection *connection,
+                                GHashTable *new_settings,
+                                GError **error)
+{
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (new_settings != NULL, FALSE);
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	g_hash_table_remove_all (NM_CONNECTION_GET_PRIVATE (connection)->settings);
+	g_hash_table_foreach (new_settings, parse_one_setting, connection);
+
+	return nm_connection_verify (connection, error);
+}
+
+typedef struct {
+	NMConnection *other;
+	gboolean failed;
+	NMSettingCompareFlags flags;
+} CompareConnectionInfo;
+
+static void
+compare_one_setting (gpointer key, gpointer value, gpointer user_data)
+{
+	NMSetting *setting = (NMSetting *) value;
+	CompareConnectionInfo *info = (CompareConnectionInfo *) user_data;
+	NMSetting *other_setting;
+
+	if (info->failed)
+		return;
+
+	other_setting = nm_connection_get_setting (info->other, G_OBJECT_TYPE (setting));
+	if (other_setting)
+		info->failed = nm_setting_compare (setting, other_setting, info->flags) ? FALSE : TRUE;
+	else
+		info->failed = TRUE;
+}
+
+/**
+ * nm_connection_compare:
+ * @a: a #NMConnection
+ * @b: a second #NMConnection to compare with the first
+ * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
+ *
+ * Compares two #NMConnection objects for similarity, with comparison behavior
+ * modified by a set of flags.  See nm_setting_compare() for a description of
+ * each flag's behavior.
+ *
+ * Returns: %TRUE if the comparison succeeds, %FALSE if it does not
+ **/
+gboolean
+nm_connection_compare (NMConnection *a,
+                       NMConnection *b,
+                       NMSettingCompareFlags flags)
+{
+	NMConnectionPrivate *priv;
+	CompareConnectionInfo info = { b, FALSE, flags };
+
+	if (!a && !b)
+		return TRUE;
+
+	if (!a || !b)
+		return FALSE;
+
+	priv = NM_CONNECTION_GET_PRIVATE (a);
+	g_hash_table_foreach (priv->settings, compare_one_setting, &info);
+	if (info.failed == FALSE) {
+		/* compare A to B, then if that is the same compare B to A to ensure
+		 * that keys that are in B but not A will make the comparison fail.
+		 */
+		info.failed = FALSE;
+		info.other = a;
+		priv = NM_CONNECTION_GET_PRIVATE (b);
+		g_hash_table_foreach (priv->settings, compare_one_setting, &info);
+	}
+
+	return info.failed ? FALSE : TRUE;
+}
+
+typedef struct {
+	gboolean success;
+	GSList *all_settings;
+	GError **error;
+} VerifySettingsInfo;
+
+static void
+verify_one_setting (gpointer data, gpointer user_data)
+{
+	NMSetting *setting = NM_SETTING (data);
+	VerifySettingsInfo *info = (VerifySettingsInfo *) user_data;
+
+	if (info->success)
+		info->success = nm_setting_verify (setting, info->all_settings, info->error);
+}
+
+static void
+hash_values_to_slist (gpointer key, gpointer value, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+
+	*list = g_slist_prepend (*list, value);
+}
+
+/**
+ * nm_connection_verify:
+ * @connection: the #NMConnection to verify
+ * @error: location to store error, or %NULL
+ *
+ * Validates the connection and all its settings.  Each setting's properties
+ * have allowed values, and some values are dependent on other values.  For
+ * example, if a WiFi connection is security enabled, the #NMSettingWireless
+ * setting object's 'security' property must contain the setting name of the
+ * #NMSettingWirelessSecurity object, which must also be present in the 
+ * connection for the connection to be valid.  As another example, the
+ * #NMSettingWired object's 'mac-address' property must be a validly formatted
+ * MAC address.  The returned #GError contains information about which
+ * setting and which property failed validation, and how it failed validation.
+ *
+ * Returns: %TRUE if the connection is valid, %FALSE if it is not
+ **/
+gboolean
+nm_connection_verify (NMConnection *connection, GError **error)
+{
+	NMConnectionPrivate *priv;
+	NMSetting *s_con;
+	VerifySettingsInfo info;
+
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	if (!NM_IS_CONNECTION (connection)) {
+		g_set_error (error,
+				NM_SETTING_CONNECTION_ERROR,
+				NM_SETTING_CONNECTION_ERROR_UNKNOWN,
+				"invalid connection; failed verification");
+		g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	}
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	/* First, make sure there's at least 'connection' setting */
+	s_con = nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+	if (!s_con) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND,
+		             "connection setting not found");
+		return FALSE;
+	}
+
+	/* Now, run the verify function of each setting */
+	memset (&info, 0, sizeof (info));
+	info.success = TRUE;
+	info.error = error;
+	g_hash_table_foreach (priv->settings, hash_values_to_slist, &info.all_settings);
+
+	g_slist_foreach (info.all_settings, verify_one_setting, &info);
+	g_slist_free (info.all_settings);
+	return info.success;
+}
+
+/**
+ * nm_connection_update_secrets:
+ * @connection: the #NMConnection
+ * @setting_name: the setting object name to which the secrets apply
+ * @secrets: a #GHashTable mapping string:#GValue of setting property names and
+ * secrets
+ * @error: location to store error, or %NULL
+ *
+ * Update the specified setting's secrets, given a hash table of secrets
+ * intended for that setting (deserialized from D-Bus for example).
+ * 
+ * Returns: %TRUE if the secrets were successfully updated and the connection
+ * is valid, %FALSE on failure or if the setting was never added to the connection
+ **/
+gboolean
+nm_connection_update_secrets (NMConnection *connection,
+                              const char *setting_name,
+                              GHashTable *secrets,
+                              GError **error)
+{
+	NMSetting *setting;
+	gboolean success;
+
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (setting_name != NULL, FALSE);
+	g_return_val_if_fail (secrets != NULL, FALSE);
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	setting = nm_connection_get_setting (connection, nm_connection_lookup_setting_type (setting_name));
+	if (!setting) {
+		g_set_error (error, NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND,
+		             "%s", setting_name);
+		return FALSE;
+	}
+
+	success = nm_setting_update_secrets (setting, secrets, error);
+	if (success)
+		g_signal_emit (connection, signals[SECRETS_UPDATED], 0, setting_name);
+	return success;
+}
+
+static gint
+setting_priority_compare (gconstpointer a, gconstpointer b)
+{
+	guint32 prio_a, prio_b;
+
+	prio_a = get_priority_for_setting_type (G_OBJECT_TYPE (NM_SETTING (a)));
+	prio_b = get_priority_for_setting_type (G_OBJECT_TYPE (NM_SETTING (b)));
+
+	if (prio_a < prio_b)
+		return -1;
+	else if (prio_a == prio_b)
+		return 0;
+	return 1;
+}
+
+static void
+add_setting_to_list (gpointer key, gpointer data, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+
+	*list = g_slist_insert_sorted (*list, NM_SETTING (data), setting_priority_compare);
+}
+
+/**
+ * nm_connection_need_secrets:
+ * @connection: the #NMConnection
+ * @hints: the address of a pointer to a #GPtrArray, initialized to NULL, which
+ * on return points to an allocated #GPtrArray containing the property names of
+ * secrets of the #NMSetting which may be required; the caller owns the array
+ * and must free the each array element with g_free(), as well as the array
+ * itself with g_ptr_array_free()
+ *
+ * Returns the name of the first setting object in the connection which would
+ * need secrets to make a successful connection.  The returned hints are only
+ * intended as a guide to what secrets may be required, because in some
+ * circumstances, there is no way to conclusively determine exactly which
+ * secrets are needed.
+ *
+ * Returns: the setting name of the #NMSetting object which has invalid or
+ * missing secrets
+ **/
+const char *
+nm_connection_need_secrets (NMConnection *connection,
+                            GPtrArray **hints)
+{
+	NMConnectionPrivate *priv;
+	GSList *settings = NULL;
+	GSList *iter;
+	char *name = NULL;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	if (hints)
+		g_return_val_if_fail (*hints == NULL, NULL);
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	/* Get list of settings in priority order */
+	g_hash_table_foreach (priv->settings, add_setting_to_list, &settings);
+
+	for (iter = settings; iter; iter = g_slist_next (iter)) {
+		NMSetting *setting = NM_SETTING (iter->data);
+		GPtrArray *secrets;
+
+		// FIXME: do something with requested secrets rather than asking for
+		// all of them.  Maybe make secrets a hash table mapping
+		// settings name :: [list of secrets key names].
+		secrets = nm_setting_need_secrets (setting);
+		if (secrets) {
+			if (hints)
+				*hints = secrets;
+			else
+				g_ptr_array_free (secrets, TRUE);
+
+			name = (char *) nm_setting_get_name (setting);
+			break;
+		}
+	}
+
+	g_slist_free (settings);
+	return name;
+}
+
+static void
+clear_setting_secrets (gpointer key, gpointer data, gpointer user_data)
+{
+	nm_setting_clear_secrets (NM_SETTING (data));
+}
+
+/**
+ * nm_connection_clear_secrets:
+ * @connection: the #NMConnection
+ *
+ * Clears and frees any secrets that may be stored in the connection, to avoid
+ * keeping secret data in memory when not needed.
+ **/
+void
+nm_connection_clear_secrets (NMConnection *connection)
+{
+	NMConnectionPrivate *priv;
+
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+	g_hash_table_foreach (priv->settings, clear_setting_secrets, NULL);
+}
+
+static void
+add_one_setting_to_hash (gpointer key, gpointer data, gpointer user_data)
+{
+	NMSetting *setting = (NMSetting *) data;
+	GHashTable *connection_hash = (GHashTable *) user_data;
+	GHashTable *setting_hash;
+
+	g_return_if_fail (setting != NULL);
+	g_return_if_fail (connection_hash != NULL);
+
+	setting_hash = nm_setting_to_hash (setting);
+	if (setting_hash)
+		g_hash_table_insert (connection_hash,
+							 g_strdup (nm_setting_get_name (setting)),
+							 setting_hash);
+}
+
+/**
+ * nm_connection_to_hash:
+ * @connection: the #NMConnection
+ *
+ * Converts the #NMConnection into a #GHashTable describing the connection,
+ * suitable for marshalling over D-Bus or serializing.  The hash table mapping
+ * is string:#GHashTable with each element in the returned hash representing
+ * a #NMSetting object.  The keys are setting object names, and the values
+ * are #GHashTables mapping string:GValue, each of which represents the
+ * properties of the #NMSetting object.
+ *
+ * Returns: a new #GHashTable describing the connection, its settings, and
+ * each setting's properties.  The caller owns the hash table and must unref
+ * the hash table with g_hash_table_unref() when it is no longer needed.
+ **/
+GHashTable *
+nm_connection_to_hash (NMConnection *connection)
+{
+	NMConnectionPrivate *priv;
+	GHashTable *connection_hash;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	connection_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+									 g_free, (GDestroyNotify) g_hash_table_destroy);
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+	g_hash_table_foreach (priv->settings, add_one_setting_to_hash, connection_hash);
+
+	/* Don't send empty hashes */
+	if (g_hash_table_size (connection_hash) < 1) {
+		g_hash_table_destroy (connection_hash);
+		connection_hash = NULL;
+	}
+
+	return connection_hash;
+}
+
+typedef struct ForEachValueInfo {
+	NMSettingValueIterFn func;
+	gpointer user_data;
+} ForEachValueInfo;
+
+static void
+for_each_setting (gpointer key, gpointer value, gpointer user_data)
+{
+	ForEachValueInfo *info = (ForEachValueInfo *) user_data;
+
+	nm_setting_enumerate_values (NM_SETTING (value), info->func, info->user_data);
+}
+
+/**
+ * nm_connection_for_each_setting_value:
+ * @connection: the #NMConnection
+ * @func: user-supplied function called for each setting's property
+ * @user_data: user data passed to @func at each invocation
+ *
+ * Iterates over the properties of each #NMSetting object in the #NMConnection,
+ * calling the supplied user function for each property.
+ **/
+void
+nm_connection_for_each_setting_value (NMConnection *connection,
+                                      NMSettingValueIterFn func,
+                                      gpointer user_data)
+{
+	NMConnectionPrivate *priv;
+	ForEachValueInfo *info;
+
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+	g_return_if_fail (func != NULL);
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	info = g_slice_new0 (ForEachValueInfo);
+	if (!info) {
+		g_warning ("Not enough memory to enumerate values.");
+		return;
+	}
+	info->func = func;
+	info->user_data = user_data;
+
+	g_hash_table_foreach (priv->settings, for_each_setting, info);
+
+	g_slice_free (ForEachValueInfo, info);
+}
+
+static void
+dump_setting (gpointer key, gpointer value, gpointer user_data)
+{
+	char *str;
+
+	str = nm_setting_to_string (NM_SETTING (value));
+	g_print ("%s\n", str);
+	g_free (str);
+}
+
+/**
+ * nm_connection_dump:
+ * @connection: the #NMConnection
+ *
+ * Print the connection to stdout.  For debugging purposes ONLY, should NOT
+ * be used for serialization of the connection or machine-parsed in any way. The
+ * output format is not guaranteed to be stable and may change at any time.
+ **/
+void
+nm_connection_dump (NMConnection *connection)
+{
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+
+	g_hash_table_foreach (NM_CONNECTION_GET_PRIVATE (connection)->settings, dump_setting, NULL);
+}
+
+/**
+ * nm_connection_set_scope:
+ * @connection: the #NMConnection
+ * @scope: the scope of the connection
+ *
+ * Sets the scope of the connection.  This property is not serialized, and is
+ * only for the reference of the caller.  A connection may have no scope
+ * (internal, temporary connections), "system" scope (provided by the system
+ * settings service), or "user" scope, provided by a user settings service.  The
+ * creator of the #NMConnection object is responsible for setting the
+ * connection's scope if needed.  Sets the #NMConnection:scope property.
+ **/
+void
+nm_connection_set_scope (NMConnection *connection, NMConnectionScope scope)
+{
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+
+	NM_CONNECTION_GET_PRIVATE (connection)->scope = scope;
+}
+
+/**
+ * nm_connection_get_scope:
+ * @connection: the #NMConnection
+ *
+ * Returns the connection scope.
+ *
+ * Returns: the scope of the connection, previously set by a call to
+ * nm_connection_set_scope().
+ **/
+NMConnectionScope
+nm_connection_get_scope (NMConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NM_CONNECTION_SCOPE_UNKNOWN);
+
+	return NM_CONNECTION_GET_PRIVATE (connection)->scope;
+}
+
+/**
+ * nm_connection_set_path:
+ * @connection: the #NMConnection
+ * @path: the D-Bus path of the connection as given by the settings service
+ * which provides the connection
+ *
+ * Sets the D-Bus path of the connection.  This property is not serialized, and
+ * is only for the reference of the caller.  Sets the #NMConnection:path
+ * property.
+ **/
+void
+nm_connection_set_path (NMConnection *connection, const char *path)
+{
+	NMConnectionPrivate *priv;
+
+	g_return_if_fail (NM_IS_CONNECTION (connection));
+
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	if (priv->path) {
+		g_free (priv->path);
+		priv->path = NULL;
+	}
+
+	if (path)
+		priv->path = g_strdup (path);
+}
+
+/**
+ * nm_connection_get_path:
+ * @connection: the #NMConnection
+ *
+ * Returns the connection's D-Bus path.
+ *
+ * Returns: the D-Bus path of the connection, previously set by a call to
+ * nm_connection_set_path().
+ **/
+const char *
+nm_connection_get_path (NMConnection *connection)
+{
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	return NM_CONNECTION_GET_PRIVATE (connection)->path;
+}
+
+/**
+ * nm_connection_new:
+ *
+ * Creates a new #NMConnection object with no #NMSetting objects.
+ *
+ * Returns: the new empty #NMConnection object
+ **/
+NMConnection *
+nm_connection_new (void)
+{
+	GObject *object;
+
+	if (!registered_settings)
+		register_default_settings ();
+
+	object = g_object_new (NM_TYPE_CONNECTION, NULL);
+
+	return NM_CONNECTION (object);
+}
+
+/**
+ * nm_connection_new_from_hash:
+ * @hash: the #GHashTable describing the connection
+ * @error: on unsuccessful return, an error
+ *
+ * Creates a new #NMConnection from a hash table describing the connection.  See
+ * nm_connection_to_hash() for a description of the expected hash table.
+ *
+ * Returns: the new #NMConnection object, populated with settings created
+ * from the values in the hash table, or NULL if the connection failed to
+ * validate
+ **/
+NMConnection *
+nm_connection_new_from_hash (GHashTable *hash, GError **error)
+{
+	NMConnection *connection;
+
+	g_return_val_if_fail (hash != NULL, NULL);
+
+	connection = nm_connection_new ();
+	g_hash_table_foreach (hash, parse_one_setting, connection);
+
+	if (!nm_connection_verify (connection, error)) {
+		g_object_unref (connection);
+		return NULL;
+	}
+
+	return connection;
+}
+
+static void
+duplicate_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	nm_connection_add_setting (NM_CONNECTION (user_data), nm_setting_duplicate (NM_SETTING (value)));
+}
+
+/**
+ * nm_connection_duplicate:
+ * @connection: the #NMConnection to duplicate
+ *
+ * Duplicates a #NMConnection.
+ *
+ * Returns: a new #NMConnection containing the same settings and properties
+ * as the source #NMConnection
+ **/
+NMConnection *
+nm_connection_duplicate (NMConnection *connection)
+{
+	NMConnection *dup;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	dup = nm_connection_new ();
+	nm_connection_set_scope (dup, nm_connection_get_scope (connection));
+	nm_connection_set_path (dup, nm_connection_get_path (connection));
+	g_hash_table_foreach (NM_CONNECTION_GET_PRIVATE (connection)->settings, duplicate_cb, dup);
+
+	return dup;
+}
+
+static void
+nm_connection_init (NMConnection *connection)
+{
+	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMConnection *connection = NM_CONNECTION (object);
+	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	g_hash_table_destroy (priv->settings);
+	priv->settings = NULL;
+
+	g_free (priv->path);
+	priv->path = NULL;
+
+	G_OBJECT_CLASS (nm_connection_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMConnection *connection = NM_CONNECTION (object);
+
+	switch (prop_id) {
+	case PROP_SCOPE:
+		nm_connection_set_scope (connection, g_value_get_uint (value));
+		break;
+	case PROP_PATH:
+		nm_connection_set_path (connection, g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMConnection *connection = NM_CONNECTION (object);
+
+	switch (prop_id) {
+	case PROP_SCOPE:
+		g_value_set_uint (value, nm_connection_get_scope (connection));
+		break;
+	case PROP_PATH:
+		g_value_set_string (value, nm_connection_get_path (connection));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_connection_class_init (NMConnectionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMConnectionPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* Properties */
+
+	/**
+	 * NMConnection:scope:
+	 *
+	 * The connection's scope, used only by the calling process as a record
+	 * of which settings service the connection is provided by.  One of the
+	 * NM_CONNECTION_SCOPE_* defines.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SCOPE,
+		 g_param_spec_uint (NM_CONNECTION_SCOPE,
+						    "Scope",
+						    "Scope",
+						    NM_CONNECTION_SCOPE_UNKNOWN,
+						    NM_CONNECTION_SCOPE_USER,
+						    NM_CONNECTION_SCOPE_UNKNOWN,
+						    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/**
+	 * NMConnection:path:
+	 *
+	 * The connection's D-Bus path, used only by the calling process as a record
+	 * of the D-Bus path of the connection as provided by a settings service.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PATH,
+		 g_param_spec_string (NM_CONNECTION_PATH,
+						  "Path",
+						  "Path",
+						  NULL,
+						  G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/* Signals */
+
+	/**
+	* NMConnection::secrets-updated:
+	* @connection: the object on which the signal is emitted
+	* @setting_name: the setting name of the #NMSetting for which secrets were
+	* updated
+	*
+	* The ::secrets-updated signal is emitted when the secrets of a setting
+	* have been changed.
+	*/
+	signals[SECRETS_UPDATED] =
+		g_signal_new ("secrets-updated",
+					  G_OBJECT_CLASS_TYPE (object_class),
+					  G_SIGNAL_RUN_FIRST,
+					  G_STRUCT_OFFSET (NMConnectionClass, secrets_updated),
+					  NULL, NULL,
+					  g_cclosure_marshal_VOID__STRING,
+					  G_TYPE_NONE, 1,
+					  G_TYPE_STRING);
+}
+
diff --git a/libnm-util/nm-connection.h b/libnm-util/nm-connection.h
new file mode 100644
index 00000000..576db049
--- /dev/null
+++ b/libnm-util/nm-connection.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_CONNECTION_H
+#define NM_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_CONNECTION            (nm_connection_get_type ())
+#define NM_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTION, NMConnection))
+#define NM_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONNECTION, NMConnectionClass))
+#define NM_IS_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONNECTION))
+#define NM_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_CONNECTION))
+#define NM_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTION, NMConnectionClass))
+
+/**
+ * NMConnectionScope:
+ * @NM_CONNECTION_SCOPE_UNKNOWN: scope not known or not yet set
+ * @NM_CONNECTION_SCOPE_SYSTEM: connection is provided by the system settings
+ *   service
+ * @NM_CONNECTION_SCOPE_USER: connection is provided by a user settings service
+ *
+ * Connection scope indicated what settings service, if any, provides the
+ * connection.
+ *
+ **/
+typedef enum {
+	NM_CONNECTION_SCOPE_UNKNOWN = 0,
+	NM_CONNECTION_SCOPE_SYSTEM,
+	NM_CONNECTION_SCOPE_USER
+} NMConnectionScope;
+
+
+/**
+ * NMConnectionError:
+ * @NM_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND: the #NMConnection object
+ *   did not contain the required #NMSettingConnection object, which must be
+ *   present for all connections
+ *
+ * Describes errors that may result from operations involving a #NMConnection.
+ *
+ **/
+typedef enum
+{
+	NM_CONNECTION_ERROR_UNKNOWN = 0,
+	NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND
+} NMConnectionError;
+
+#define NM_TYPE_CONNECTION_ERROR (nm_connection_error_get_type ()) 
+GType nm_connection_error_get_type (void);
+
+#define NM_CONNECTION_ERROR nm_connection_error_quark ()
+GQuark nm_connection_error_quark (void);
+
+#define NM_CONNECTION_SCOPE "scope"
+#define NM_CONNECTION_PATH "path"
+
+/**
+ * NMConnection:
+ *
+ * The NMConnection struct contains only private data.
+ * It should only be accessed through the functions described below.
+ */
+typedef struct {
+	GObject parent;
+} NMConnection;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Signals */
+	void (*secrets_updated) (NMConnection *connection, const char * setting);
+} NMConnectionClass;
+
+GType nm_connection_get_type (void);
+
+NMConnection *nm_connection_new           (void);
+
+NMConnection *nm_connection_new_from_hash (GHashTable *hash, GError **error);
+
+NMConnection *nm_connection_duplicate     (NMConnection *connection);
+
+void          nm_connection_add_setting   (NMConnection *connection,
+								   NMSetting    *setting);
+
+void          nm_connection_remove_setting (NMConnection *connection,
+                                            GType         setting_type);
+
+NMSetting    *nm_connection_get_setting   (NMConnection *connection,
+                                           GType         setting_type);
+
+NMSetting    *nm_connection_get_setting_by_name (NMConnection *connection,
+									    const char *name);
+
+gboolean      nm_connection_replace_settings (NMConnection *connection,
+                                              GHashTable *new_settings,
+                                              GError **error);
+
+gboolean      nm_connection_compare       (NMConnection *a,
+                                           NMConnection *b,
+                                           NMSettingCompareFlags flags);
+
+gboolean      nm_connection_verify        (NMConnection *connection, GError **error);
+
+const char *  nm_connection_need_secrets  (NMConnection *connection,
+                                           GPtrArray **hints);
+
+void          nm_connection_clear_secrets (NMConnection *connection);
+
+gboolean      nm_connection_update_secrets (NMConnection *connection,
+                                            const char *setting_name,
+                                            GHashTable *secrets,
+                                            GError **error);
+
+void             nm_connection_set_scope (NMConnection *connection,
+                                                 NMConnectionScope scope);
+
+NMConnectionScope nm_connection_get_scope (NMConnection *connection);
+
+void             nm_connection_set_path (NMConnection *connection,
+                                         const char *path);
+
+const char *     nm_connection_get_path (NMConnection *connection);
+
+void          nm_connection_for_each_setting_value (NMConnection *connection,
+										  NMSettingValueIterFn func,
+										  gpointer user_data);
+
+GHashTable   *nm_connection_to_hash       (NMConnection *connection);
+void          nm_connection_dump          (NMConnection *connection);
+
+NMSetting    *nm_connection_create_setting (const char *name);
+
+GType nm_connection_lookup_setting_type (const char *name);
+
+GType nm_connection_lookup_setting_type_by_quark (GQuark error_quark);
+
+G_END_DECLS
+
+#endif /* NM_CONNECTION_H */
diff --git a/libnm-util/nm-param-spec-specialized.c b/libnm-util/nm-param-spec-specialized.c
new file mode 100644
index 00000000..f5a362ce
--- /dev/null
+++ b/libnm-util/nm-param-spec-specialized.c
@@ -0,0 +1,973 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include "nm-param-spec-specialized.h"
+
+struct _NMParamSpecSpecialized {
+	GParamSpec parent;
+};
+
+#include <string.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-dbus-glib-types.h"
+
+/***********************************************************/
+/* nm_gvalues_compare */
+
+static gint nm_gvalues_compare (const GValue *value1, const GValue *value2);
+
+static gboolean
+type_is_fixed_size (GType type, gsize *tsize)
+{
+	switch (type) {
+	case G_TYPE_CHAR:
+		if (tsize) *tsize = sizeof (char);
+		return TRUE;
+	case G_TYPE_UCHAR:
+		if (tsize) *tsize = sizeof (guchar);
+		return TRUE;
+	case G_TYPE_BOOLEAN:
+		if (tsize) *tsize = sizeof (gboolean);
+		return TRUE;
+	case G_TYPE_LONG:
+		if (tsize) *tsize = sizeof (glong);
+		return TRUE;
+	case G_TYPE_ULONG:
+		if (tsize) *tsize = sizeof (gulong);
+		return TRUE;
+	case G_TYPE_INT:
+		if (tsize) *tsize = sizeof (gint);
+		return TRUE;
+	case G_TYPE_UINT:
+		if (tsize) *tsize = sizeof (guint);
+		return TRUE;
+	case G_TYPE_INT64:
+		if (tsize) *tsize = sizeof (gint64);
+		return TRUE;
+	case G_TYPE_UINT64:
+		if (tsize) *tsize = sizeof (guint64);
+		return TRUE;
+	case G_TYPE_FLOAT:
+		if (tsize) *tsize = sizeof (gfloat);
+		return TRUE;
+	case G_TYPE_DOUBLE:
+		if (tsize) *tsize = sizeof (gdouble);
+		return TRUE;
+	default:
+		return FALSE;
+	}
+}
+
+#define FLOAT_FACTOR 0.00000001
+
+static gint
+nm_gvalues_compare_fixed (const GValue *value1, const GValue *value2)
+{
+	int ret = 0;
+
+	switch (G_VALUE_TYPE (value1)) {
+	case G_TYPE_CHAR: {
+		gchar val1 = g_value_get_char (value1);
+		gchar val2 = g_value_get_char (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_UCHAR: {
+		guchar val1 = g_value_get_uchar (value1);
+		guchar val2 = g_value_get_uchar (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_BOOLEAN: {
+		gboolean val1 = g_value_get_boolean (value1);
+		gboolean val2 = g_value_get_boolean (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_LONG: {
+		glong val1 = g_value_get_long (value1);
+		glong val2 = g_value_get_long (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_ULONG: {
+		gulong val1 = g_value_get_ulong (value1);
+		gulong val2 = g_value_get_ulong (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_INT: {
+		gint val1 = g_value_get_int (value1);
+		gint val2 = g_value_get_int (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_UINT: {
+		guint val1 = g_value_get_uint (value1);
+		guint val2 = g_value_get_uint (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_INT64: {
+		gint64 val1 = g_value_get_int64 (value1);
+		gint64 val2 = g_value_get_int64 (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_UINT64: {
+		guint64 val1 = g_value_get_uint64 (value1);
+		guint64 val2 = g_value_get_uint64 (value2);
+		if (val1 != val2)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_FLOAT: {
+		gfloat val1 = g_value_get_float (value1);
+		gfloat val2 = g_value_get_float (value2);
+		/* Can't use == or != here due to inexactness of FP */
+		if (fabsf (val1 - val2) > FLOAT_FACTOR)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	case G_TYPE_DOUBLE: {
+		gdouble val1 = g_value_get_double (value1);
+		gdouble val2 = g_value_get_double (value2);
+		if (fabs (val1 - val2) > FLOAT_FACTOR)
+			ret = val1 < val2 ? -1 : val1 > val2;
+		break;
+	}
+	default:
+		g_warning ("Unhandled fixed size type '%s'", G_VALUE_TYPE_NAME (value1));
+	}
+
+	return ret;
+}
+
+static gint
+nm_gvalues_compare_string (const GValue *value1, const GValue *value2)
+{
+	const char *str1 = g_value_get_string (value1);
+	const char *str2 = g_value_get_string (value2);
+
+	if (str1 == str2)
+		return 0;
+
+	if (!str1)
+		return 1;
+	if (!str2)
+		return -1;
+
+	return strcmp (str1, str2);
+}
+
+static gint
+nm_gvalues_compare_strv (const GValue *value1, const GValue *value2)
+{
+	char **strv1;
+	char **strv2;
+	gint ret;
+	guint i = 0;
+
+	strv1 = (char **) g_value_get_boxed (value1);
+	strv2 = (char **) g_value_get_boxed (value2);
+
+	while (strv1[i] && strv2[i]) {
+		ret = strcmp (strv1[i], strv2[i]);
+		if (ret)
+			return ret;
+		i++;
+	}
+
+	if (strv1[i] == NULL && strv2[i] == NULL)
+		return 0;
+
+	if (strv1[i])
+		return 1;
+
+	return -1;
+}
+
+static void
+nm_gvalue_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static GValue *
+nm_gvalue_dup (const GValue *value)
+{
+	GValue *dup;
+
+	dup = g_slice_new0 (GValue);
+	g_value_init (dup, G_VALUE_TYPE (value));
+	g_value_copy (value, dup);
+
+	return dup;
+}
+
+static void
+iterate_collection (const GValue *value, gpointer user_data)
+{
+	GSList **list = (GSList **) user_data;
+	
+	*list = g_slist_prepend (*list, nm_gvalue_dup (value));
+}
+
+static gint
+nm_gvalues_compare_collection (const GValue *value1, const GValue *value2)
+{
+	gint ret;
+	guint len1;
+	guint len2;
+	GType value_type = dbus_g_type_get_collection_specialization (G_VALUE_TYPE (value1));
+	gsize element_size = 0;
+
+	if (type_is_fixed_size (value_type, &element_size)) {
+		gpointer data1 = NULL;
+		gpointer data2 = NULL;
+
+		dbus_g_type_collection_get_fixed ((GValue *) value1, &data1, &len1);
+		dbus_g_type_collection_get_fixed ((GValue *) value2, &data2, &len2);
+
+		if (len1 != len2)
+			ret = len1 < len2 ? -1 : len1 > len2;
+		else
+			ret = memcmp (data1, data2, len1 * element_size);
+	} else {
+		GSList *list1 = NULL;
+		GSList *list2 = NULL;
+
+		dbus_g_type_collection_value_iterate (value1, iterate_collection, &list1);
+		len1 = g_slist_length (list1);
+		dbus_g_type_collection_value_iterate (value2, iterate_collection, &list2);
+		len2 = g_slist_length (list2);
+
+		if (len1 != len2)
+			ret = len1 < len2 ? -1 : len1 > len2;
+		else {
+			GSList *iter1;
+			GSList *iter2;
+
+			for (iter1 = list1, iter2 = list2, ret = 0;
+				ret == 0 && iter1 && iter2; 
+				iter1 = iter1->next, iter2 = iter2->next)
+				ret = nm_gvalues_compare ((GValue *) iter1->data, (GValue *) iter2->data);
+		}
+
+		g_slist_foreach (list1, (GFunc) nm_gvalue_destroy, NULL);
+		g_slist_free (list1);
+		g_slist_foreach (list2, (GFunc) nm_gvalue_destroy, NULL);
+		g_slist_free (list2);
+	}
+
+	return ret;
+}
+
+static void
+iterate_map (const GValue *key_val,
+		   const GValue *value_val,
+		   gpointer user_data)
+{
+	GHashTable **hash = (GHashTable **) user_data;
+
+	g_hash_table_insert (*hash, g_value_dup_string (key_val), nm_gvalue_dup (value_val));
+}
+
+typedef struct {
+	GHashTable *hash2;
+	gint ret;
+} CompareMapInfo;
+
+static void
+compare_one_map_item (gpointer key, gpointer val, gpointer user_data)
+{
+	CompareMapInfo *info = (CompareMapInfo *) user_data;
+	GValue *value2;
+
+	if (info->ret)
+		return;
+
+	value2 = (GValue *) g_hash_table_lookup (info->hash2, key);
+	if (value2)
+		info->ret = nm_gvalues_compare ((GValue *) val, value2);
+	else
+		info->ret = 1;
+}
+
+static gint
+nm_gvalues_compare_map (const GValue *value1, const GValue *value2)
+{
+	GHashTable *hash1 = NULL;
+	GHashTable *hash2 = NULL;
+	guint len1;
+	guint len2;
+	gint ret = 0;
+
+	if (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1)) != G_TYPE_STRING) {
+		g_warning ("Can not compare maps with '%s' for keys",
+				 g_type_name (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1))));
+		return 0;
+	}
+
+	hash1 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
+	dbus_g_type_map_value_iterate (value1, iterate_map, &hash1); 
+	len1 = g_hash_table_size (hash1);
+
+	hash2 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
+	dbus_g_type_map_value_iterate (value2, iterate_map, &hash2);
+	len2 = g_hash_table_size (hash2);
+
+	if (len1 != len2)
+		ret = len1 < len2 ? -1 : len1 > len2;
+	else {
+		CompareMapInfo info;
+
+		info.ret = 0;
+		info.hash2 = hash2;
+		g_hash_table_foreach (hash1, compare_one_map_item, &info);
+		ret = info.ret;
+	}
+
+	g_hash_table_destroy (hash1);
+	g_hash_table_destroy (hash2);
+
+	return ret;
+}
+
+static gint
+nm_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2)
+{
+	GValueArray *values1, *values2;
+	GValue *tmp_val;
+	GByteArray *addr1, *addr2;
+	guint32 prefix1, prefix2;
+	GByteArray *gw1, *gw2;
+	gint ret = 0;
+	int i;
+
+	/* IP6 addresses are GValueArrays (see nm-dbus-glib-types.h) */
+	values1 = g_value_get_boxed (value1);
+	values2 = g_value_get_boxed (value2);
+
+	/* Since they are NM IPv6 address structures, we expect both
+	 * to contain two elements as specified in nm-dbus-glib-types.h.
+	 */
+	g_return_val_if_fail (values1->n_values == 3, 0);
+	g_return_val_if_fail (values2->n_values == 3, 0);
+
+	/* First struct IPv6 address */
+	tmp_val = g_value_array_get_nth (values1, 0);
+	addr1 = g_value_get_boxed (tmp_val);
+	/* First struct IPv6 prefix */
+	tmp_val = g_value_array_get_nth (values1, 1);
+	prefix1 = g_value_get_uint (tmp_val);
+	/* First struct IPv6 gateway */
+	tmp_val = g_value_array_get_nth (values1, 2);
+	gw1 = g_value_get_boxed (tmp_val);
+
+	/* Second struct IPv6 address */
+	tmp_val = g_value_array_get_nth (values2, 0);
+	addr2 = g_value_get_boxed (tmp_val);
+	/* Second struct IPv6 prefix */
+	tmp_val = g_value_array_get_nth (values2, 1);
+	prefix2 = g_value_get_uint (tmp_val);
+	/* Second struct IPv6 gateway */
+	tmp_val = g_value_array_get_nth (values2, 2);
+	gw2 = g_value_get_boxed (tmp_val);
+
+	/* Compare IPv6 addresses */
+	if (prefix1 != prefix2)
+		return prefix1 < prefix2 ? -1 : prefix1 > prefix2;
+
+	if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)addr1->data, (struct in6_addr *)addr2->data)) {
+		for (i = 0; ret == 0 && i < addr1->len; i++)
+			ret = addr1->data[i] < addr2->data[i] ? -1 : addr1->data[i] > addr2->data[i];
+	}
+
+	if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *) gw1->data, (struct in6_addr *) gw2->data)) {
+		for (i = 0; ret == 0 && i < gw1->len; i++)
+			ret = gw1->data[i] < gw2->data[i] ? -1 : gw1->data[i] > gw2->data[i];
+	}
+
+	return ret;
+}
+
+static gint
+nm_gvalue_ip6_route_compare (const GValue *value1, const GValue *value2)
+{
+	GValueArray *values1, *values2;
+	GValue *tmp_val;
+	GByteArray *dest1, *dest2;
+	GByteArray *next_hop1, *next_hop2;
+	guint32 prefix1, prefix2;
+	guint32 metric1, metric2;
+	gint ret = 0;
+	int i;
+
+	/* IP6 routes are GValueArrays (see nm-dbus-glib-types.h) */
+	values1 = g_value_get_boxed (value1);
+	values2 = g_value_get_boxed (value2);
+
+	/* Since they are NM IPv6 route structures, we expect both
+	 * to contain 4 elements as specified in nm-dbus-glib-types.h.
+	 */
+	g_return_val_if_fail (values1->n_values == 4, 0);
+	g_return_val_if_fail (values2->n_values == 4, 0);
+
+	/* First struct IPv6 route */
+	tmp_val = g_value_array_get_nth (values1, 0);
+	dest1 = g_value_get_boxed (tmp_val);
+	tmp_val = g_value_array_get_nth (values1, 1);
+	prefix1 = g_value_get_uint (tmp_val);
+	tmp_val = g_value_array_get_nth (values1, 2);
+	next_hop1 = g_value_get_boxed (tmp_val);
+	tmp_val = g_value_array_get_nth (values1, 3);
+	metric1 = g_value_get_uint (tmp_val);
+
+	/* Second struct IPv6 route */
+	tmp_val = g_value_array_get_nth (values2, 0);
+	dest2 = g_value_get_boxed (tmp_val);
+	tmp_val = g_value_array_get_nth (values2, 1);
+	prefix2 = g_value_get_uint (tmp_val);
+	tmp_val = g_value_array_get_nth (values2, 2);
+	next_hop2 = g_value_get_boxed (tmp_val);
+	tmp_val = g_value_array_get_nth (values2, 3);
+	metric2 = g_value_get_uint (tmp_val);
+
+	/* Compare the routes */
+	if (prefix1 != prefix2)
+		return prefix1 < prefix2 ? -1 : prefix1 > prefix2;
+
+	if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)dest1->data, (struct in6_addr *)dest2->data)) {
+		for (i = 0; ret == 0 && i < dest1->len; i++)
+			ret = dest1->data[i] < dest2->data[i] ? -1 : dest1->data[i] > dest2->data[i];
+	}
+
+	if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)next_hop1->data, (struct in6_addr *)next_hop2->data)) {
+		for (i = 0; ret == 0 && i < next_hop1->len; i++)
+			ret = next_hop1->data[i] < next_hop2->data[i] ? -1 : next_hop1->data[i] > next_hop2->data[i];
+	}
+
+	if (metric1 != metric2)
+		ret = metric1 < metric2 ? -1 : metric1 > metric2;
+
+	return ret;
+}
+
+static gint
+nm_gvalues_compare_struct (const GValue *value1, const GValue *value2)
+{
+	/* value1 and value2 must contain the same type since
+	 * nm_gvalues_compare() enforced that already.
+	 */
+
+	if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ADDRESS)) {
+		return nm_gvalue_ip6_address_compare (value1, value2);
+	} else if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ROUTE)) {
+		return nm_gvalue_ip6_route_compare (value1, value2);
+	} else {
+		g_warning ("Don't know how to compare structures");
+		return (value1 == value2);
+	}
+}
+
+gint
+nm_gvalues_compare (const GValue *value1, const GValue *value2)
+{
+	GType type1;
+	GType type2;
+	gint ret;
+
+	if (value1 == value2)
+		return 0;
+	if (!value1)
+		return 1;
+	if (!value2)
+		return -1;
+
+	type1 = G_VALUE_TYPE (value1);
+	type2 = G_VALUE_TYPE (value2);
+
+	if (type1 != type2)
+		return type1 < type2 ? -1 : type1 > type2;
+
+	if (type_is_fixed_size (type1, NULL))
+		ret = nm_gvalues_compare_fixed (value1, value2);
+	else if (type1 == G_TYPE_STRING) 
+		ret = nm_gvalues_compare_string (value1, value2);
+	else if (G_VALUE_HOLDS_BOXED (value1)) {
+		gpointer p1 = g_value_get_boxed (value1);
+		gpointer p2 = g_value_get_boxed (value2);
+
+		if (p1 == p2)
+			ret = 0; /* Exactly the same values */
+		else if (!p1)
+			ret = 1; /* The comparision functions below don't handle NULLs */
+		else if (!p2)
+			ret = -1; /* The comparision functions below don't handle NULLs */
+		else if (type1 == G_TYPE_STRV)
+			ret = nm_gvalues_compare_strv (value1, value2);
+		else if (dbus_g_type_is_collection (type1))
+			ret = nm_gvalues_compare_collection (value1, value2);
+		else if (dbus_g_type_is_map (type1))
+			ret = nm_gvalues_compare_map (value1, value2);
+		else if (dbus_g_type_is_struct (type1))
+			ret = nm_gvalues_compare_struct (value1, value2);
+		else if (type1 == G_TYPE_VALUE)
+			ret = nm_gvalues_compare ((GValue *) g_value_get_boxed (value1), (GValue *) g_value_get_boxed (value2));
+		else {
+			g_warning ("Don't know how to compare boxed types '%s'", g_type_name (type1));
+			ret = value1 == value2;
+		}
+	} else {
+		g_warning ("Don't know how to compare types '%s'", g_type_name (type1));
+		ret = value1 == value2;
+	}
+
+	return ret;
+}
+
+/***********************************************************/
+
+static void
+param_specialized_init (GParamSpec *pspec)
+{
+}
+
+static void
+param_specialized_set_default (GParamSpec *pspec, GValue *value)
+{
+	value->data[0].v_pointer = NULL;
+}
+
+static gboolean
+param_specialized_validate (GParamSpec *pspec, GValue *value)
+{
+	NMParamSpecSpecialized *sspec = NM_PARAM_SPEC_SPECIALIZED (pspec);
+	GType value_type = G_VALUE_TYPE (value);
+	gboolean changed = FALSE;
+
+	if (!g_value_type_compatible (value_type, G_PARAM_SPEC_VALUE_TYPE (sspec))) {
+		g_value_reset (value);
+		changed = TRUE;
+	}
+
+	return changed;
+}
+
+static gint
+param_specialized_values_cmp (GParamSpec *pspec,
+						const GValue *value1,
+						const GValue *value2)
+{
+	return nm_gvalues_compare (value1, value2);
+}
+
+GType
+_nm_param_spec_specialized_get_type (void)
+{
+	static GType type;
+
+	if (G_UNLIKELY (type) == 0) {
+		static const GParamSpecTypeInfo pspec_info = {
+			sizeof (NMParamSpecSpecialized),
+			0,
+			param_specialized_init,
+			G_TYPE_OBJECT, /* value_type */
+			NULL,          /* finalize */
+			param_specialized_set_default,
+			param_specialized_validate,
+			param_specialized_values_cmp,
+		};
+		type = g_param_type_register_static ("NMParamSpecSpecialized", &pspec_info);
+	}
+
+	return type;
+}
+
+GParamSpec *
+_nm_param_spec_specialized (const char *name,
+					  const char *nick,
+					  const char *blurb,
+					  GType specialized_type,
+					  GParamFlags flags)
+{
+	NMParamSpecSpecialized *pspec;
+
+	g_return_val_if_fail (g_type_is_a (specialized_type, G_TYPE_BOXED), NULL);
+
+	pspec = g_param_spec_internal (NM_TYPE_PARAM_SPEC_SPECIALIZED,
+							 name, nick, blurb, flags);
+
+	G_PARAM_SPEC (pspec)->value_type = specialized_type;
+
+	return G_PARAM_SPEC (pspec);
+}
+
+/***********************************************************/
+/* Tests */
+
+#if 0
+
+static void
+compare_ints (void)
+{
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+
+	g_value_init (&value1, G_TYPE_INT);
+	g_value_init (&value2, G_TYPE_INT);
+
+	g_value_set_int (&value1, 5);
+	g_value_set_int (&value2, 5);
+	g_print ("Comparing ints 5 and 5: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_int (&value2, 10);
+	g_print ("Comparing ints 5 and 10: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_int (&value2, 1);
+	g_print ("Comparing ints 5 and 1: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_strings (void)
+{
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+	const char *str1 = "hello";
+	const char *str2 = "world";
+
+	g_value_init (&value1, G_TYPE_STRING);
+	g_value_init (&value2, G_TYPE_STRING);
+
+	g_value_set_string (&value1, str1);
+	g_value_set_string (&value2, str1);
+	g_print ("Comparing identical strings: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_string (&value2, str2);
+	g_print ("Comparing different strings: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_strv (void)
+{
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+	char *strv1[] = { "foo", "bar", "baz", NULL };
+	char *strv2[] = { "foo", "bar", "bar", NULL };
+	char *strv3[] = { "foo", "bar", NULL };
+	char *strv4[] = { "foo", "bar", "baz", "bam", NULL };
+
+	g_value_init (&value1, G_TYPE_STRV);
+	g_value_init (&value2, G_TYPE_STRV);
+
+	g_value_set_boxed (&value1, strv1);
+	g_value_set_boxed (&value2, strv1);
+	g_print ("Comparing identical strv's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_boxed (&value2, strv2);
+	g_print ("Comparing different strv's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_boxed (&value2, strv3);
+	g_print ("Comparing different len (smaller) strv's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_boxed (&value2, strv4);
+	g_print ("Comparing different len (longer) strv's: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_garrays (void)
+{
+	GArray *array1;
+	GArray *array2;
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+	int i;
+
+	g_value_init (&value1, DBUS_TYPE_G_UINT_ARRAY);
+	array1 = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+	g_value_init (&value2, DBUS_TYPE_G_UINT_ARRAY);
+	array2 = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+	for (i = 0; i < 5; i++) {
+		g_array_append_val (array1, i);
+		g_array_append_val (array2, i);
+	}
+
+	g_value_set_boxed (&value1, array1);
+	g_value_set_boxed (&value2, array2);
+
+	g_print ("Comparing identical arrays's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_array_remove_index (array2, 0);
+	g_value_set_boxed (&value2, array2);
+	g_print ("Comparing different length arrays's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	i = 7;
+	g_array_prepend_val (array2, i);
+	g_value_set_boxed (&value2, array2);
+	g_print ("Comparing different arrays's: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_ptrarrays (void)
+{
+	GPtrArray *array1;
+	GPtrArray *array2;
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+
+	g_value_init (&value1, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING));
+	array1 = g_ptr_array_new ();
+
+	g_value_init (&value2, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING));
+	array2 = g_ptr_array_new ();
+
+	g_ptr_array_add (array1, "hello");
+	g_ptr_array_add (array1, "world");
+	g_value_set_boxed (&value1, array1);
+
+	g_ptr_array_add (array2, "hello");
+	g_ptr_array_add (array2, "world");
+	g_value_set_boxed (&value2, array2);
+
+	g_print ("Comparing identical ptr arrays's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_ptr_array_add (array2, "boo");
+	g_value_set_boxed (&value2, array2);
+	g_print ("Comparing different len ptr arrays's: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_ptr_array_add (array1, "booz");
+	g_value_set_boxed (&value1, array1);
+	g_print ("Comparing different ptr arrays's: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_str_hash (void)
+{
+	GHashTable *hash1;
+	GHashTable *hash2;
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+
+	g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
+	g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
+
+	hash1 = g_hash_table_new (g_str_hash, g_str_equal);
+	hash2 = g_hash_table_new (g_str_hash, g_str_equal);
+
+	g_hash_table_insert (hash1, "key1", "hello");
+	g_hash_table_insert (hash1, "key2", "world");
+
+	g_hash_table_insert (hash2, "key1", "hello");
+	g_hash_table_insert (hash2, "key2", "world");
+
+	g_value_set_boxed (&value1, hash1);
+	g_value_set_boxed (&value2, hash2);
+	g_print ("Comparing identical str hashes: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_hash_table_remove (hash2, "key2");
+	g_value_set_boxed (&value2, hash2);
+	g_print ("Comparing different length str hashes: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_hash_table_insert (hash2, "key2", "moon");
+	g_value_set_boxed (&value2, hash2);
+	g_print ("Comparing different str hashes: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static GValue *
+str_to_gvalue (const char *str)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_STRING);
+	g_value_set_string (value, str);
+
+	return value;
+}
+
+static GValue *
+int_to_gvalue (int i)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_INT);
+	g_value_set_int (value, i);
+
+	return value;
+}
+
+static void
+compare_gvalue_hash (void)
+{
+	GHashTable *hash1;
+	GHashTable *hash2;
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+
+	g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
+	g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
+
+	hash1 = g_hash_table_new (g_str_hash, g_str_equal);
+	hash2 = g_hash_table_new (g_str_hash, g_str_equal);
+
+	g_hash_table_insert (hash1, "key1", str_to_gvalue ("hello"));
+	g_hash_table_insert (hash1, "key2", int_to_gvalue (5));
+
+	g_hash_table_insert (hash2, "key1", str_to_gvalue ("hello"));
+	g_hash_table_insert (hash2, "key2", int_to_gvalue (5));
+
+	g_value_set_boxed (&value1, hash1);
+	g_value_set_boxed (&value2, hash2);
+	g_print ("Comparing identical gvalue hashes: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_hash_table_remove (hash2, "key2");
+	g_value_set_boxed (&value2, hash2);
+	g_print ("Comparing different length str hashes: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_hash_table_insert (hash2, "key2", str_to_gvalue ("moon"));
+	g_value_set_boxed (&value2, hash2);
+	g_print ("Comparing different str hashes: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_ip6_addresses (void)
+{
+	GValueArray *array1;
+	GValueArray *array2;
+	GValueArray *array3;
+	GByteArray *ba1;
+	GByteArray *ba2;
+	GByteArray *ba3;
+	GValue element = { 0 };
+	GValue value1 = { 0 };
+	GValue value2 = { 0 };
+	struct in6_addr addr1;
+	struct in6_addr addr2;
+	struct in6_addr addr3;
+	guint32 prefix1 = 64;
+	guint32 prefix2 = 64;
+	guint32 prefix3 = 0;
+
+	inet_pton (AF_INET6, "1:2:3:4:5:6:7:8", &addr1, sizeof (struct in6_addr));
+	inet_pton (AF_INET6, "ffff:2:3:4:5:6:7:8", &addr2, sizeof (struct in6_addr));
+	inet_pton (AF_INET6, "::", &addr3, sizeof (struct in6_addr));
+
+	/* address 1 */
+	ba1 = g_byte_array_new ();
+	array1 = g_value_array_new (2);
+	g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+	g_byte_array_append (ba1, (guint8 *) addr1.s6_addr, 16);
+	g_value_take_boxed (&element, ba1);
+	g_value_array_append (array1, &element);
+	g_value_unset (&element);
+
+	g_value_init (&element, G_TYPE_UINT);
+	g_value_set_uint (&element, prefix1);
+	g_value_array_append (array1, &element);
+	g_value_unset (&element);
+
+	/* address 2 */
+	ba2 = g_byte_array_new ();
+	array2 = g_value_array_new (2);
+	g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+	g_byte_array_append (ba2, (guint8 *) addr2.s6_addr, 16);
+	g_value_take_boxed (&element, ba2);
+	g_value_array_append (array2, &element);
+	g_value_unset (&element);
+
+	g_value_init (&element, G_TYPE_UINT);
+	g_value_set_uint (&element, prefix2);
+	g_value_array_append (array2, &element);
+	g_value_unset (&element);
+
+	/* address 3 */
+	ba3 = g_byte_array_new ();
+	array3 = g_value_array_new (2);
+	g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+	g_byte_array_append (ba3, (guint8 *) addr3.s6_addr, 16);
+	g_value_take_boxed (&element, ba3);
+	g_value_array_append (array3, &element);
+	g_value_unset (&element);
+
+	g_value_init (&element, G_TYPE_UINT);
+	g_value_set_uint (&element, prefix3);
+	g_value_array_append (array3, &element);
+	g_value_unset (&element);
+
+	g_value_init (&value1, DBUS_TYPE_G_IP6_ADDRESS);
+	g_value_init (&value2, DBUS_TYPE_G_IP6_ADDRESS);
+
+	g_value_set_boxed (&value1, array1);
+	g_value_set_boxed (&value2, array1);
+	g_print ("Comparing identical IPv6 address structures: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_boxed (&value1, array1);
+	g_value_set_boxed (&value2, array2);
+	g_print ("Comparing different IPv6 address structures: %d\n", nm_gvalues_compare (&value1, &value2));
+
+	g_value_set_boxed (&value1, array1);
+	g_value_set_boxed (&value2, array3);
+	g_print ("Comparing different IPv6 address structures: %d\n", nm_gvalues_compare (&value1, &value2));
+}
+
+int
+main (int argc, char *argv[])
+{
+	DBusGConnection *bus;
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	compare_ints ();
+	compare_strings ();
+	compare_strv ();
+	compare_garrays ();
+	compare_ptrarrays ();
+	compare_str_hash ();
+	compare_gvalue_hash ();
+	compare_ip6_addresses ();
+
+	return 0;
+}
+
+#endif
diff --git a/libnm-util/nm-param-spec-specialized.h b/libnm-util/nm-param-spec-specialized.h
new file mode 100644
index 00000000..a6173674
--- /dev/null
+++ b/libnm-util/nm-param-spec-specialized.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_PARAM_SPEC_SPECIALIZED_H
+#define NM_PARAM_SPEC_SPECIALIZED_H
+
+#include <glib-object.h>
+
+typedef struct _NMParamSpecSpecialized NMParamSpecSpecialized;
+
+#define NM_TYPE_PARAM_SPEC_SPECIALIZED (_nm_param_spec_specialized_get_type ())
+
+#define NM_IS_PARAM_SPEC_SPECIALIZED(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), NM_TYPE_PARAM_SPEC_SPECIALIZED))
+#define NM_PARAM_SPEC_SPECIALIZED(pspec)    (G_TYPE_CHECK_INSTANCE_CAST ((pspec), NM_TYPE_PARAM_SPEC_SPECIALIZED, NMParamSpecSpecialized))
+
+GType _nm_param_spec_specialized_get_type (void);
+
+GParamSpec *_nm_param_spec_specialized (const char *name,
+							    const char *nick,
+							    const char *blurb,
+							    GType specialized_type,
+							    GParamFlags flags);
+
+#endif /* NM_PARAM_SPEC_SPECIALIZED_H */
diff --git a/libnm-util/nm-setting-8021x.c b/libnm-util/nm-setting-8021x.c
new file mode 100644
index 00000000..eea6ba5e
--- /dev/null
+++ b/libnm-util/nm-setting-8021x.c
@@ -0,0 +1,3440 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <dbus/dbus-glib.h>
+#include "nm-setting-8021x.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "crypto.h"
+#include "nm-utils-private.h"
+
+/**
+ * SECTION:nm-setting-8021x
+ * @short_description: Describes 802.1x-authenticated connection properties
+ * @include: nm-setting-8021x.h
+ *
+ * The #NMSetting8021x object is a #NMSetting subclass that describes
+ * properties necessary for connection to 802.1x-authenticated networks, such as
+ * WPA and WPA2 Enterprise WiFi networks and wired 802.1x networks.  802.1x
+ * connections typically use certificates and/or EAP authentication methods to
+ * securely verify, identify, and authenticate the client to the network itself,
+ * instead of simply relying on a widely shared static key.
+ *
+ * It's a good idea to read up on wpa_supplicant configuration before using this
+ * setting extensively, since most of the options here correspond closely with
+ * the relevant wpa_supplicant configuration options.
+ *
+ * Furthermore, to get a good idea of 802.1x, EAP, TLS, TTLS, etc and their
+ * applications to WiFi and wired networks, you'll want to get copies of the
+ * following books.
+ *
+ *  802.11 Wireless Networks: The Definitive Guide, Second Edition
+ *       Author: Matthew Gast
+ *       ISBN: 978-0596100520
+ *
+ *  Cisco Wireless LAN Security
+ *       Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky
+ *       ISBN: 978-1587051548
+ **/
+
+#define SCHEME_PATH "file://"
+
+/**
+ * nm_setting_802_1x_error_quark:
+ *
+ * Registers an error quark for #NMSetting8021x if necessary.
+ *
+ * Returns: the error quark used for #NMSetting8021x errors.
+ **/
+GQuark
+nm_setting_802_1x_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-802-1x-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_802_1x_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_802_1X_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_802_1X_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSetting8021xError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING)
+
+#define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate))
+
+typedef struct {
+	GSList *eap; /* GSList of strings */
+	char *identity;
+	char *anonymous_identity;
+	GByteArray *ca_cert;
+	char *ca_path;
+	GByteArray *client_cert;
+	char *phase1_peapver;
+	char *phase1_peaplabel;
+	char *phase1_fast_provisioning;
+	char *phase2_auth;
+	char *phase2_autheap;
+	GByteArray *phase2_ca_cert;
+	char *phase2_ca_path;
+	GByteArray *phase2_client_cert;
+	char *password;
+	char *pin;
+	char *psk;
+	GByteArray *private_key;
+	char *private_key_password;
+	GByteArray *phase2_private_key;
+	char *phase2_private_key_password;
+	gboolean system_ca_certs;
+} NMSetting8021xPrivate;
+
+enum {
+	PROP_0,
+	PROP_EAP,
+	PROP_IDENTITY,
+	PROP_ANONYMOUS_IDENTITY,
+	PROP_CA_CERT,
+	PROP_CA_PATH,
+	PROP_CLIENT_CERT,
+	PROP_PHASE1_PEAPVER,
+	PROP_PHASE1_PEAPLABEL,
+	PROP_PHASE1_FAST_PROVISIONING,
+	PROP_PHASE2_AUTH,
+	PROP_PHASE2_AUTHEAP,
+	PROP_PHASE2_CA_CERT,
+	PROP_PHASE2_CA_PATH,
+	PROP_PHASE2_CLIENT_CERT,
+	PROP_PASSWORD,
+	PROP_PRIVATE_KEY,
+	PROP_PRIVATE_KEY_PASSWORD,
+	PROP_PHASE2_PRIVATE_KEY,
+	PROP_PHASE2_PRIVATE_KEY_PASSWORD,
+	PROP_PIN,
+	PROP_PSK,
+	PROP_SYSTEM_CA_CERTS,
+
+	LAST_PROP
+};
+
+/**
+ * nm_setting_802_1x_new:
+ *
+ * Creates a new #NMSetting8021x object with default values.
+ *
+ * Returns: the new empty #NMSetting8021x object
+ **/
+NMSetting *
+nm_setting_802_1x_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL);
+}
+
+/**
+ * nm_setting_802_1x_get_num_eap_methods:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the number of eap methods allowed for use when connecting to the
+ * network.  Generally only one EAP method is used.  Use the functions
+ * nm_setting_802_1x_get_eap_method(), nm_setting_802_1x_add_eap_method(),
+ * and nm_setting_802_1x_remove_eap_method() for adding, removing, and retrieving
+ * allowed EAP methods.
+ *
+ * Returns: the number of allowed EAP methods
+ **/
+guint32
+nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0);
+
+	return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->eap);
+}
+
+/**
+ * nm_setting_802_1x_get_eap_method:
+ * @setting: the #NMSetting8021x
+ * @i: the index of the EAP method name to return
+ *
+ * Returns the name of the allowed EAP method at index @i.
+ *
+ * Returns: the name of the allowed EAP method at index @i
+ **/
+const char *
+nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i)
+{
+	NMSetting8021xPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->eap), NULL);
+
+	return (const char *) g_slist_nth_data (priv->eap, i);
+}
+
+/**
+ * nm_setting_802_1x_add_eap_method:
+ * @setting: the #NMSetting8021x
+ * @eap: the name of the EAP method to allow for this connection
+ *
+ * Adds an allowed EAP method.  The setting is not valid until at least one
+ * EAP method has been added.  See #NMSetting8021x:eap property for a list of
+ * allowed EAP methods.
+ *
+ * Returns: TRUE if the EAP method was successfully added, FALSE if it was
+ *  not a valid method or if it was already allowed.
+ **/
+gboolean
+nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap)
+{
+	NMSetting8021xPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+	g_return_val_if_fail (eap != NULL, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+	for (iter = priv->eap; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (eap, (char *) iter->data))
+			return FALSE;
+	}
+
+	priv->eap = g_slist_append (priv->eap, g_ascii_strdown (eap, -1));
+	return TRUE;
+}
+
+/**
+ * nm_setting_802_1x_remove_eap_method:
+ * @setting: the #NMSetting8021x
+ * @i: the index of the EAP method to remove
+ *
+ * Removes the allowed EAP method at the specified index.
+ **/
+void
+nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i)
+{
+	NMSetting8021xPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->eap, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->eap = g_slist_delete_link (priv->eap, elt);
+}
+
+/**
+ * nm_setting_802_1x_clear_eap_methods:
+ * @setting: the #NMSetting8021x
+ *
+ * Clears all allowed EAP methods.
+ **/
+void
+nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting)
+{
+	NMSetting8021xPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+	nm_utils_slist_free (priv->eap, g_free);
+	priv->eap = NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_identity:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the identifier used by some EAP methods (like TLS) to
+ * authenticate the user.  Often this is a username or login name.
+ *
+ * Returns: the user identifier
+ **/
+const char *
+nm_setting_802_1x_get_identity (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->identity;
+}
+
+/**
+ * nm_setting_802_1x_get_anonymous_identity:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the anonymous identifier used by some EAP methods (like TTLS) to
+ * authenticate the user in the outer unencrypted "phase 1" authentication.  The
+ * inner "phase 2" authentication will use the #NMSetting8021x:identity in
+ * a secure form, if applicable for that EAP method.
+ *
+ * Returns: the anonymous identifier
+ **/
+const char *
+nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->anonymous_identity;
+}
+
+/**
+ * nm_setting_802_1x_get_ca_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the path of the CA certificate directory if previously set.  Systems
+ * will often have a directory that contains multiple individual CA certificates
+ * which the supplicant can then add to the verification chain.  This may be
+ * used in addition to the #NMSetting8021x:ca-cert property to add more CA
+ * certificates for verifying the network to client.
+ *
+ * Returns: the CA certificate directory path
+ **/
+const char *
+nm_setting_802_1x_get_ca_path (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_path;
+}
+
+/**
+ * nm_setting_802_1x_get_system_ca_certs:
+ * @setting: the #NMSetting8021x
+ *
+ * Sets the #NMSetting8021x:system-ca-certs property. The
+ * #NMSetting8021x:ca-path and #NMSetting8021x:phase2-ca-path
+ * properties are ignored if the #NMSetting8021x:system-ca-certs property is
+ * TRUE, in which case a system-wide CA certificate directory specified at
+ * compile time (using the --system-ca-path configure option) is used in place
+ * of these properties. 
+ *
+ * Returns: TRUE if a system CA certificate path should be used, FALSE if not
+ **/
+gboolean
+nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs;
+}
+
+static NMSetting8021xCKScheme
+get_cert_scheme (GByteArray *array)
+{
+	if (!array || !array->len)
+		return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+
+	if (   (array->len > strlen (SCHEME_PATH))
+	    && !memcmp (array->data, SCHEME_PATH, strlen (SCHEME_PATH)))
+		return NM_SETTING_802_1X_CK_SCHEME_PATH;
+
+	return NM_SETTING_802_1X_CK_SCHEME_BLOB;
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the CA certificate.  If the returned scheme
+ * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_ca_cert_blob();
+ * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_ca_cert_path().
+ *
+ * Returns: scheme used to store the CA certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the CA certificate blob if the CA certificate is stored using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.  Not all EAP methods use a
+ * CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: the CA certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the CA certificate blob if the CA certificate is stored using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.  Not all EAP methods use a
+ * CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_ca_cert_blob().
+ *
+ * Returns: the CA certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_ca_cert (NMSetting8021x *setting)
+{
+	return nm_setting_802_1x_get_ca_cert_blob (setting);
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the CA certificate path if the CA certificate is stored using the
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.  Not all EAP methods use a
+ * CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: path to the CA certificate file
+ **/
+const char *
+nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_ca_cert:
+ * @setting: the #NMSetting8021x
+ * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the CA certificate file
+ *   (PEM or DER format).  The path must be UTF-8 encoded; use
+ *   g_filename_to_utf8() to convert if needed.  Passing NULL with any @scheme
+ *   clears the CA certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:ca-cert property
+ * with the raw certificate data if using the %NM_SETTING_802_1X_CK_SCHEME_BLOB
+ * scheme, or with the path to the certificate file if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_ca_cert (NMSetting8021x *self,
+                               const char *value,
+                               NMSetting8021xCKScheme scheme,
+                               NMSetting8021xCKFormat *out_format,
+                               GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *data;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      FALSE);
+	}
+
+	if (out_format)
+		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Clear out any previous ca_cert blob */
+	if (priv->ca_cert) {
+		g_byte_array_free (priv->ca_cert, TRUE);
+		priv->ca_cert = NULL;
+	}
+
+	if (!value)
+		return TRUE;
+
+	data = crypto_load_and_verify_certificate (value, &format, error);
+	if (data) {
+		/* wpa_supplicant can only use raw x509 CA certs */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_format)
+				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+			break;
+		default:
+			g_byte_array_free (data, TRUE);
+			data = NULL;
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_CA_CERT);
+			break;
+		}
+
+		if (data) {
+			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+				priv->ca_cert = data;
+			else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+				/* Add the path scheme tag to the front, then the fielname */
+				priv->ca_cert = g_byte_array_sized_new (strlen (value) + strlen (SCHEME_PATH) + 1);
+				g_byte_array_append (priv->ca_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+				g_byte_array_append (priv->ca_cert, (const guint8 *) value, strlen (value));
+				g_byte_array_append (priv->ca_cert, (const guint8 *) "\0", 1);
+			} else
+				g_assert_not_reached ();
+		}
+	}
+
+	return priv->ca_cert != NULL;
+}
+
+static NMSetting8021xCKType
+ck_format_to_type (NMSetting8021xCKFormat format)
+{
+	switch (format) {
+	case NM_SETTING_802_1X_CK_FORMAT_X509:
+		return NM_SETTING_802_1X_CK_TYPE_X509;
+	case NM_SETTING_802_1X_CK_FORMAT_RAW_KEY:
+		return NM_SETTING_802_1X_CK_TYPE_RAW_KEY;
+	case NM_SETTING_802_1X_CK_FORMAT_PKCS12:
+		return NM_SETTING_802_1X_CK_TYPE_PKCS12;
+	default:
+		break;
+	}
+	return NM_SETTING_802_1X_CK_TYPE_UNKNOWN;
+}
+
+/**
+ * nm_setting_802_1x_set_ca_cert_from_file:
+ * @setting: the #NMSetting8021x
+ * @filename: the path of the CA certificate file (PEM or DER format). Passing
+ *   NULL clears the CA certificate.
+ * @out_ck_type: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:ca-cert property
+ * with the raw certificate data using the %NM_SETTING_802_1X_CK_SCHEME_BLOB
+ * scheme.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_set_ca_cert() with the
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_ca_cert_from_file (NMSetting8021x *setting,
+                                         const char *filename,
+                                         NMSetting8021xCKType *out_ck_type,
+                                         GError **error)
+{
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	success = nm_setting_802_1x_set_ca_cert (setting,
+	                                         filename,
+	                                         NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	                                         &format,
+	                                         error);
+	if (success && out_ck_type)
+		*out_ck_type = ck_format_to_type (format);
+
+	return success;
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the client certificate.  If the returned scheme
+ * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_client_cert_blob();
+ * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the client certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: the client certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_client_cert_blob().
+ *
+ * Returns: the client certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_client_cert (NMSetting8021x *setting)
+{
+	return nm_setting_802_1x_get_client_cert_blob (setting);
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the client certificate file
+ **/
+const char *
+nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_client_cert:
+ * @setting: the #NMSetting8021x
+ * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the client certificate
+ *   file (PEM, DER, or PKCS#12 format).  The path must be UTF-8 encoded; use
+ *   g_filename_to_utf8() to convert if needed.  Passing NULL with any @scheme
+ *   clears the client certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:client-cert
+ * property with the raw certificate data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_client_cert (NMSetting8021x *self,
+                                   const char *value,
+                                   NMSetting8021xCKScheme scheme,
+                                   NMSetting8021xCKFormat *out_format,
+                                   GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *data;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      FALSE);
+	}
+
+	if (out_format)
+		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Clear out any previous ca_cert blob */
+	if (priv->client_cert) {
+		g_byte_array_free (priv->client_cert, TRUE);
+		priv->client_cert = NULL;
+	}
+
+	if (!value)
+		return TRUE;
+
+	data = crypto_load_and_verify_certificate (value, &format, error);
+	if (data) {
+		/* wpa_supplicant can only use raw x509 CA certs */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_format)
+				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			if (out_format)
+				*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+			break;
+		default:
+			g_byte_array_free (data, TRUE);
+			data = NULL;
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_CLIENT_CERT);
+			break;
+		}
+
+		if (data) {
+			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+				priv->client_cert = data;
+			else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+				/* Add the path scheme tag to the front, then the fielname */
+				priv->client_cert = g_byte_array_sized_new (strlen (value) + strlen (SCHEME_PATH) + 1);
+				g_byte_array_append (priv->client_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+				g_byte_array_append (priv->client_cert, (const guint8 *) value, strlen (value));
+				g_byte_array_append (priv->client_cert, (const guint8 *) "\0", 1);
+			} else
+				g_assert_not_reached ();
+		}
+	}
+
+	return priv->client_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_set_client_cert_from_file:
+ * @setting: the #NMSetting8021x
+ * @filename: the path of the client certificate file (PEM, DER, or
+ *   PKCS#12 format).  Passing NULL clears the client certificate.
+ * @out_ck_type: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:client-cert
+ * property with the raw certificate data.
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_set_client_cert() with the
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_client_cert_from_file (NMSetting8021x *setting,
+                                             const char *filename,
+                                             NMSetting8021xCKType *out_ck_type,
+                                             GError **error)
+{
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	success = nm_setting_802_1x_set_client_cert (setting,
+	                                             filename,
+	                                             NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	                                             &format,
+	                                             error);
+	if (success && out_ck_type)
+		*out_ck_type = ck_format_to_type (format);
+
+	return success;
+}
+
+/**
+ * nm_setting_802_1x_get_phase1_peapver:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the "phase 1" PEAP version to be used when authenticating with
+ *  EAP-PEAP as contained in the #NMSetting8021x:phase1-peapver property.  Valid
+ *  values are NULL (unset), "0" (PEAP version 0), and "1" (PEAP version 1).
+ **/
+const char *
+nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peapver;
+}
+
+/**
+ * nm_setting_802_1x_get_phase1_peaplabel:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: whether the "phase 1" PEAP label is new-style or old-style, to be
+ *  used when authenticating with EAP-PEAP, as contained in the
+ *  #NMSetting8021x:phase1-peaplabel property.  Valid values are NULL (unset),
+ *  "0" (use old-style label), and "1" (use new-style label).  See the
+ *  wpa_supplicant documentation for more details.
+ **/
+const char *
+nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peaplabel;
+}
+
+/**
+ * nm_setting_802_1x_get_phase1_fast_provisioning:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: whether "phase 1" PEAP fast provisioning should be used, as specified
+ *  by the #NMSetting8021x:phase1-fast-provisioning property.  See the
+ *  wpa_supplicant documentation for more details.
+ **/
+const char *
+nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_fast_provisioning;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_auth:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the "phase 2" non-EAP (ex MD5) allowed authentication method as
+ *   specified by the #NMSetting8021x:phase2-auth property.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_auth;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_autheap:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the "phase 2" EAP-based (ex TLS) allowed authentication method as
+ *   specified by the #NMSetting8021x:phase2-autheap property.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_autheap;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the path of the "phase 2" CA certificate directory if previously set.
+ * Systems will often have a directory that contains multiple individual CA
+ * certificates which the supplicant can then add to the verification chain.
+ * This may be used in addition to the #NMSetting8021x:phase2-ca-cert property
+ * to add more CA certificates for verifying the network to client.
+ *
+ * Returns: the "phase 2" CA certificate directory path
+ **/
+const char *
+nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_path;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the "phase 2" CA certificate.  If the
+ * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_ca_cert_blob(); if %NM_SETTING_802_1X_CK_SCHEME_PATH,
+ * use nm_setting_802_1x_get_ca_cert_path().
+ *
+ * Returns: scheme used to store the "phase 2" CA certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the "phase 2" CA certificate blob if the CA certificate is stored
+ * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.  Not all EAP methods use
+ * a CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: the "phase 2" CA certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the "phase 2" CA certificate blob.  Not all EAP methods use
+ * a CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_phase2_ca_cert_blob().
+ *
+ * Returns: the "phase 2" CA certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_ca_cert (NMSetting8021x *setting)
+{
+	return nm_setting_802_1x_get_phase2_ca_cert_blob (setting);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the "phase 2" CA certificate path if the CA certificate is stored
+ * using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.  Not all EAP methods use
+ * a CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset.  Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: path to the "phase 2" CA certificate file
+ **/
+const char *
+nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_ca_cert:
+ * @setting: the #NMSetting8021x
+ * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" CA
+ *   certificate file (PEM or DER format).  The path must be UTF-8 encoded; use
+ *   g_filename_to_utf8() to convert if needed.  Passing NULL with any @scheme
+ *   clears the "phase2" CA certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:phase2-ca-cert
+ * property with the raw certificate data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *self,
+                                      const char *value,
+                                      NMSetting8021xCKScheme scheme,
+                                      NMSetting8021xCKFormat *out_format,
+                                      GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *data;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      FALSE);
+	}
+
+	if (out_format)
+		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Clear out any previous ca_cert blob */
+	if (priv->phase2_ca_cert) {
+		g_byte_array_free (priv->phase2_ca_cert, TRUE);
+		priv->phase2_ca_cert = NULL;
+	}
+
+	if (!value)
+		return TRUE;
+
+	data = crypto_load_and_verify_certificate (value, &format, error);
+	if (data) {
+		/* wpa_supplicant can only use raw x509 CA certs */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_format)
+				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+			break;
+		default:
+			g_byte_array_free (data, TRUE);
+			data = NULL;
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_CA_CERT);
+			break;
+		}
+
+		if (data) {
+			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+				priv->phase2_ca_cert = data;
+			else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+				/* Add the path scheme tag to the front, then the fielname */
+				priv->phase2_ca_cert = g_byte_array_sized_new (strlen (value) + strlen (SCHEME_PATH) + 1);
+				g_byte_array_append (priv->phase2_ca_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+				g_byte_array_append (priv->phase2_ca_cert, (const guint8 *) value, strlen (value));
+				g_byte_array_append (priv->phase2_ca_cert, (const guint8 *) "\0", 1);
+			} else
+				g_assert_not_reached ();
+		}
+	}
+
+	return priv->phase2_ca_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_ca_cert_from_file:
+ * @setting: the #NMSetting8021x
+ * @filename: the path of the "phase2" CA certificate file (PEM or DER format).
+ *   Passing NULL with any @scheme clears the "phase2" CA certificate.
+ * @out_ck_type: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:phase2-ca-cert
+ * property with the raw certificate data.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_set_phase2_ca_cert().
+ *   with the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_ca_cert_from_file (NMSetting8021x *setting,
+                                                const char *filename,
+                                                NMSetting8021xCKType *out_ck_type,
+                                                GError **error)
+{
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	success = nm_setting_802_1x_set_phase2_ca_cert (setting,
+	                                                filename,
+	                                                NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	                                                &format,
+	                                                error);
+	if (success && out_ck_type)
+		*out_ck_type = ck_format_to_type (format);
+
+	return success;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the "phase 2" client certificate.  If the
+ * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_client_cert_blob(); if
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
+ * nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the "phase 2" client certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: the "phase 2" client certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_phase2_client_cert_blob().
+ *
+ * Returns: the "phase 2" client certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_client_cert (NMSetting8021x *setting)
+{
+	return nm_setting_802_1x_get_phase2_client_cert_blob (setting);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the "phase 2" client certificate file
+ **/
+const char *
+nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_client_cert:
+ * @setting: the #NMSetting8021x
+ * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" client
+ *   certificate file (PEM, DER, or PKCS#12 format).  The path must be UTF-8
+ *   encoded; use g_filename_to_utf8() to convert if needed.  Passing NULL with
+ *   any @scheme clears the "phase2" client certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:phase2-client-cert
+ * property with the raw certificate data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *self,
+                                          const char *value,
+                                          NMSetting8021xCKScheme scheme,
+                                          NMSetting8021xCKFormat *out_format,
+                                          GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *data;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      FALSE);
+	}
+
+	if (out_format)
+		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Clear out any previous ca_cert blob */
+	if (priv->phase2_client_cert) {
+		g_byte_array_free (priv->phase2_client_cert, TRUE);
+		priv->phase2_client_cert = NULL;
+	}
+
+	if (!value)
+		return TRUE;
+
+	data = crypto_load_and_verify_certificate (value, &format, error);
+	if (data) {
+		/* wpa_supplicant can only use raw x509 CA certs */
+		switch (format) {
+		case NM_CRYPTO_FILE_FORMAT_X509:
+			if (out_format)
+				*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+			break;
+		case NM_CRYPTO_FILE_FORMAT_PKCS12:
+			if (out_format)
+				*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+			break;
+		default:
+			g_byte_array_free (data, TRUE);
+			data = NULL;
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+			break;
+		}
+
+		if (data) {
+			if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+				priv->phase2_client_cert = data;
+			else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+				/* Add the path scheme tag to the front, then the fielname */
+				priv->phase2_client_cert = g_byte_array_sized_new (strlen (value) + strlen (SCHEME_PATH) + 1);
+				g_byte_array_append (priv->phase2_client_cert, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+				g_byte_array_append (priv->phase2_client_cert, (const guint8 *) value, strlen (value));
+				g_byte_array_append (priv->phase2_client_cert, (const guint8 *) "\0", 1);
+			} else
+				g_assert_not_reached ();
+		}
+	}
+
+	return priv->phase2_client_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_client_cert_from_file:
+ * @setting: the #NMSetting8021x
+ * @filename: pass the path of the "phase2" client certificate file (PEM, DER,
+ *   or PKCS#12 format).  Passing NULL clears the "phase2" client certificate.
+ * @out_ck_type: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:phase2-client-cert
+ * property with the raw certificate data.
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_set_phase2_client_cert() with the.
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *setting,
+                                                    const char *filename,
+                                                    NMSetting8021xCKType *out_ck_type,
+                                                    GError **error)
+{
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	success = nm_setting_802_1x_set_phase2_client_cert (setting,
+	                                                    filename,
+	                                                    NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	                                                    &format,
+	                                                    error);
+	if (success && out_ck_type)
+		*out_ck_type = ck_format_to_type (format);
+
+	return success;
+}
+
+/**
+ * nm_setting_802_1x_get_password:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the password used by the authentication method, if any, as specified
+ *   by the #NMSetting8021x:password property
+ **/
+const char *
+nm_setting_802_1x_get_password (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->password;
+}
+
+/**
+ * nm_setting_802_1x_get_pin:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the PIN used by the authentication method, if any, as specified
+ *   by the #NMSetting8021x:pin property
+ **/
+const char *
+nm_setting_802_1x_get_pin (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->pin;
+}
+
+/**
+ * nm_setting_802_1x_get_psk:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the Pre-Shared-Key used by the authentication method, if any, as
+ *   specified by the #NMSetting8021x:psk property
+ **/
+const char *
+nm_setting_802_1x_get_psk (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->psk;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the private key.  If the returned scheme is
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_client_cert_blob(); if
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
+ * nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the private key (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key);
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: the private key data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_private_key_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_private_key_blob().
+ *
+ * Returns: the private key data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_private_key (NMSetting8021x *setting)
+{
+	return nm_setting_802_1x_get_private_key_blob (setting);
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the private key file
+ **/
+const char *
+nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_private_key_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_private_key:
+ * @setting: the #NMSetting8021x
+ * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the private key file
+ *   (PEM, DER, or PKCS#12 format).  The path must be UTF-8 encoded; use
+ *   g_filename_to_utf8() to convert if needed.  Passing NULL with any @scheme
+ *   clears the private key.
+ * @password: password used to decrypt the private key
+ * @scheme: desired storage scheme for the private key
+ * @out_format: on successful return, the type of the private key added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a private key from disk and sets the #NMSetting8021x:private-key
+ * property with the raw private key data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the private key
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_private_key (NMSetting8021x *self,
+                                   const char *value,
+                                   const char *password,
+                                   NMSetting8021xCKScheme scheme,
+                                   NMSetting8021xCKFormat *out_format,
+                                   GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	GByteArray *data;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      FALSE);
+	}
+
+	if (out_format)
+		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Clear out any previous private key blob */
+	if (priv->private_key) {
+		/* Try not to leave the private key around in memory */
+		memset (priv->private_key, 0, priv->private_key->len);
+		g_byte_array_free (priv->private_key, TRUE);
+		priv->private_key = NULL;
+	}
+
+	g_free (priv->private_key_password);
+	priv->private_key_password = NULL;
+
+	if (!value)
+		return TRUE;
+
+	/* Verify the key and the private key password */
+	data = crypto_get_private_key (value,
+	                               password,
+	                               &key_type,
+	                               &format,
+	                               error);
+	if (!data) {
+		/* As a special case for private keys, even if the decrypt fails,
+		 * return the key's file type.
+		 */
+		if (out_format && crypto_is_pkcs12_file (value, NULL))
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+
+		return FALSE;
+	}
+
+	switch (format) {
+	case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
+		if (out_format)
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+		break;
+	case NM_CRYPTO_FILE_FORMAT_X509:
+		if (out_format)
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+		break;
+	case NM_CRYPTO_FILE_FORMAT_PKCS12:
+		if (out_format)
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		break;
+	default:
+		memset (data->data, 0, data->len);
+		g_byte_array_free (data, TRUE);
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PRIVATE_KEY);
+		return FALSE;
+	}
+
+	g_assert (data);
+	if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+		priv->private_key = data;
+		data = NULL;
+
+		/* Always update the private key for blob + pkcs12 since the
+		 * pkcs12 files are encrypted
+		 */
+		if (format == NM_CRYPTO_FILE_FORMAT_PKCS12)
+			priv->private_key_password = g_strdup (password);
+	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+		/* Add the path scheme tag to the front, then the fielname */
+		priv->private_key = g_byte_array_sized_new (strlen (value) + strlen (SCHEME_PATH) + 1);
+		g_byte_array_append (priv->private_key, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+		g_byte_array_append (priv->private_key, (const guint8 *) value, strlen (value));
+		g_byte_array_append (priv->private_key, (const guint8 *) "\0", 1);
+
+		/* Always update the private key with paths since the key the
+		 * cert refers to is encrypted.
+		 */
+		priv->private_key_password = g_strdup (password);
+	} else
+		g_assert_not_reached ();
+
+	/* Clear and free private key data if it's no longer needed */
+	if (data) {
+		memset (data->data, 0, data->len);
+		g_byte_array_free (data, TRUE);
+	}
+
+	/* As required by NM and wpa_supplicant, set the client-cert
+	 * property to the same PKCS#12 data.
+	 */
+	if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) {
+		if (priv->client_cert)
+			g_byte_array_free (priv->client_cert, TRUE);
+
+		priv->client_cert = g_byte_array_sized_new (priv->private_key->len);
+		g_byte_array_append (priv->client_cert, priv->private_key->data, priv->private_key->len);
+	}
+
+	return priv->private_key != NULL;
+}
+
+/**
+ * nm_setting_802_1x_set_private_key_from_file:
+ * @setting: the #NMSetting8021x
+ * @filename: the path of the private key file (PEM, DER, or PKCS#12 format).
+ *   Passing NULL clears the private key.
+ * @password: password used to decrypt the private key
+ * @out_ck_type: on successful return, the type of the private key added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a private key from disk and sets the #NMSetting8021x:private-key
+ * property with the raw private key data.
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_set_private_key() with.
+ *   the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_private_key_from_file (NMSetting8021x *setting,
+                                             const char *filename,
+                                             const char *password,
+                                             NMSetting8021xCKType *out_ck_type,
+                                             GError **error)
+{
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	success = nm_setting_802_1x_set_private_key (setting,
+	                                             filename,
+	                                             password,
+	                                             NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	                                             &format,
+	                                             error);
+	if (success && out_ck_type)
+		*out_ck_type = ck_format_to_type (format);
+
+	return success;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_password:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the private key password used to decrypt the private key if
+ *  previously set with nm_setting_802_1x_set_private_key_from_file(),
+ *  nm_setting_802_1x_set_private_key_path(), or the
+ *  #NMSetting8021x:private-key-password property.
+ **/
+const char *
+nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_format:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the data format of the private key data stored in the
+ *   #NMSetting8021x:private-key property
+ **/
+NMSetting8021xCKFormat
+nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting)
+{
+	NMSetting8021xPrivate *priv;
+	const char *path;
+	GError *error = NULL;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+	if (!priv->private_key)
+		return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		if (crypto_is_pkcs12_data (priv->private_key))
+			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		return NM_SETTING_802_1X_CK_FORMAT_X509;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = nm_setting_802_1x_get_private_key_path (setting);
+		if (crypto_is_pkcs12_file (path, &error))
+			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		if (error) {
+			/* Couldn't read the file or something */
+			g_error_free (error);
+			return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+		}
+		return NM_SETTING_802_1X_CK_FORMAT_X509;
+	default:
+		break;
+	}
+
+	return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_type:
+ * @setting: the #NMSetting8021x
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_private_key_format().
+ *
+ * Returns: the data format of the private key data stored in the
+ *   #NMSetting8021x:private-key property
+ **/
+NMSetting8021xCKType
+nm_setting_802_1x_get_private_key_type (NMSetting8021x *setting)
+{
+	return ck_format_to_type (nm_setting_802_1x_get_private_key_format (setting));
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_password:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the private key password used to decrypt the private key if
+ *  previously set with nm_setting_802_1x_set_phase2_private_key_from_file(),
+ *  nm_setting_802_1x_set_phase2_private_key_path(), or the
+ *  #NMSetting8021x:phase2-private-key-password property.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the "phase 2" private key.  If the returned
+ * scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_client_cert_blob(); if
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
+ * nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the "phase 2" private key (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+	return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: the "phase 2" private key data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+	return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_private_key_blob().
+ *
+ * Returns: the "phase 2" private key data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_private_key (NMSetting8021x *setting)
+{
+	return nm_setting_802_1x_get_phase2_private_key_blob (setting);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the "phase 2" private key file
+ **/
+const char *
+nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting)
+{
+	NMSetting8021xCKScheme scheme;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+	scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
+	g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+	return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_private_key:
+ * @setting: the #NMSetting8021x
+ * @value: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ *   %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" private 
+ *   key file (PEM, DER, or PKCS#12 format).  The path must be UTF-8 encoded;
+ *   use g_filename_to_utf8() to convert if needed.  Passing NULL with any
+ *   @scheme clears the "phase2" private key.
+ * @password: password used to decrypt the private key
+ * @scheme: desired storage scheme for the private key
+ * @out_format: on successful return, the type of the private key added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a "phase 2" private key from disk and sets the
+ * #NMSetting8021x:phase2-private-key property with the raw private key data if
+ * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the
+ * private key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *self,
+                                          const char *value,
+                                          const char *password,
+                                          NMSetting8021xCKScheme scheme,
+                                          NMSetting8021xCKFormat *out_format,
+                                          GError **error)
+{
+	NMSetting8021xPrivate *priv;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	GByteArray *data;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (self), FALSE);
+
+	if (value) {
+		g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
+		g_return_val_if_fail (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+		                      || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+		                      FALSE);
+	}
+
+	if (out_format)
+		g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+	priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Clear out any previous private key blob */
+	if (priv->phase2_private_key) {
+		/* Try not to leave the private key around in memory */
+		memset (priv->phase2_private_key, 0, priv->phase2_private_key->len);
+		g_byte_array_free (priv->phase2_private_key, TRUE);
+		priv->phase2_private_key = NULL;
+	}
+
+	g_free (priv->phase2_private_key_password);
+	priv->phase2_private_key_password = NULL;
+
+	if (!value)
+		return TRUE;
+
+	/* Verify the key and the private key password */
+	data = crypto_get_private_key (value,
+	                               password,
+	                               &key_type,
+	                               &format,
+	                               error);
+	if (!data) {
+		/* As a special case for private keys, even if the decrypt fails,
+		 * return the key's file type.
+		 */
+		if (out_format && crypto_is_pkcs12_file (value, NULL))
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+
+		return FALSE;
+	}
+
+	switch (format) {
+	case NM_CRYPTO_FILE_FORMAT_RAW_KEY:
+		if (out_format)
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+		break;
+	case NM_CRYPTO_FILE_FORMAT_X509:
+		if (out_format)
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+		break;
+	case NM_CRYPTO_FILE_FORMAT_PKCS12:
+		if (out_format)
+			*out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		break;
+	default:
+		memset (data->data, 0, data->len);
+		g_byte_array_free (data, TRUE);
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+		return FALSE;
+	}
+
+	g_assert (data);
+	if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+		priv->phase2_private_key = data;
+		data = NULL;
+
+		/* Always update the private key for blob + pkcs12 since the
+		 * pkcs12 files are encrypted
+		 */
+		if (format == NM_CRYPTO_FILE_FORMAT_PKCS12)
+			priv->phase2_private_key_password = g_strdup (password);
+	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+		/* Add the path scheme tag to the front, then the fielname */
+		priv->phase2_private_key = g_byte_array_sized_new (strlen (value) + strlen (SCHEME_PATH) + 1);
+		g_byte_array_append (priv->phase2_private_key, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+		g_byte_array_append (priv->phase2_private_key, (const guint8 *) value, strlen (value));
+		g_byte_array_append (priv->phase2_private_key, (const guint8 *) "\0", 1);
+
+		/* Always update the private key with paths since the key the
+		 * cert refers to is encrypted.
+		 */
+		priv->phase2_private_key_password = g_strdup (password);
+	} else
+		g_assert_not_reached ();
+
+	/* Clear and free private key data if it's no longer needed */
+	if (data) {
+		memset (data->data, 0, data->len);
+		g_byte_array_free (data, TRUE);
+	}
+
+	/* As required by NM and wpa_supplicant, set the client-cert
+	 * property to the same PKCS#12 data.
+	 */
+	if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) {
+		if (priv->phase2_client_cert)
+			g_byte_array_free (priv->phase2_client_cert, TRUE);
+
+		priv->phase2_client_cert = g_byte_array_sized_new (priv->phase2_private_key->len);
+		g_byte_array_append (priv->phase2_client_cert, priv->phase2_private_key->data, priv->phase2_private_key->len);
+	}
+
+	return priv->phase2_private_key != NULL;
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_private_key_from_file:
+ * @setting: the #NMSetting8021x
+ * @filename: the path of the "phase2" private key file (PEM, DER, or PKCS#12
+ *   format).  Passing NULL clears the "phase2" private key.
+ * @password: password used to decrypt the private key
+ * @out_ck_type: on successful return, the type of the private key added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a "phase 2" private key from disk and sets the
+ * #NMSetting8021x:phase2-private-key property with the raw private key data.
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_set_phase2_private_key() with
+ *   the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme.
+ *
+ * Returns: TRUE if the operation succeeded, FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *setting,
+                                                    const char *filename,
+                                                    const char *password,
+                                                    NMSetting8021xCKType *out_ck_type,
+                                                    GError **error)
+{
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	success = nm_setting_802_1x_set_phase2_private_key (setting,
+	                                                    filename,
+	                                                    password,
+	                                                    NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	                                                    &format,
+	                                                    error);
+	if (success && out_ck_type)
+		*out_ck_type = ck_format_to_type (format);
+
+	return success;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_format:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the data format of the "phase 2" private key data stored in the
+ *   #NMSetting8021x:phase2-private-key property
+ **/
+NMSetting8021xCKFormat
+nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting)
+{
+	NMSetting8021xPrivate *priv;
+	const char *path;
+	GError *error = NULL;
+
+	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
+	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+	if (!priv->phase2_private_key)
+		return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		if (crypto_is_pkcs12_data (priv->phase2_private_key))
+			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		return NM_SETTING_802_1X_CK_FORMAT_X509;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		path = nm_setting_802_1x_get_phase2_private_key_path (setting);
+		if (crypto_is_pkcs12_file (path, &error))
+			return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+		if (error) {
+			/* Couldn't read the file or something */
+			g_error_free (error);
+			return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+		}
+		return NM_SETTING_802_1X_CK_FORMAT_X509;
+	default:
+		break;
+	}
+
+	return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_type:
+ * @setting: the #NMSetting8021x
+ *
+ * Deprecated: 0.8: This function has been deprecated and should
+ *   not be used in newly written code. Calling this function is
+ *   equivalent to calling nm_setting_802_1x_get_phase2_private_key_format().
+ *
+ * Returns: the data format of the private key data stored in the
+ *   #NMSetting8021x:phase2-private-key property
+ **/
+NMSetting8021xCKType
+nm_setting_802_1x_get_phase2_private_key_type (NMSetting8021x *setting)
+{
+	return ck_format_to_type (nm_setting_802_1x_get_phase2_private_key_format (setting));
+}
+
+static void
+need_secrets_password (NMSetting8021x *self,
+                       GPtrArray *secrets,
+                       gboolean phase2)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	if (!priv->password || !strlen (priv->password))
+		g_ptr_array_add (secrets, NM_SETTING_802_1X_PASSWORD);
+}
+
+static void
+need_secrets_sim (NMSetting8021x *self,
+                  GPtrArray *secrets,
+                  gboolean phase2)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	if (!priv->pin || !strlen (priv->pin))
+		g_ptr_array_add (secrets, NM_SETTING_802_1X_PIN);
+}
+
+static gboolean
+need_private_key_password (const GByteArray *blob,
+                           const char *path,
+                           const char *password)
+{
+	/* Private key password is only un-needed if the private key scheme is BLOB,
+	 * because BLOB keys are decrypted by the settings service.  A private key
+	 * password is required if the private key is PKCS#12 format, or if the
+	 * private key scheme is PATH.
+	 */
+	if (path) {
+		GByteArray *tmp;
+		NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+		NMCryptoFileFormat key_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+
+		/* check the password */
+		tmp = crypto_get_private_key (path, password, &key_type, &key_format, NULL);
+		if (tmp) {
+			/* Decrypt/verify successful; password must be OK */
+			g_byte_array_free (tmp, TRUE);
+			return FALSE;
+		}
+	} else if (blob) {
+		/* Non-PKCS#12 blob-scheme keys are already decrypted by their settings
+		 * service, thus if the private key is not PKCS#12 format, a new password
+		 * is not required.  If the PKCS#12 key can be decrypted with the given
+		 * password, then we don't need a new password either.
+		 */
+		if (!crypto_is_pkcs12_data (blob) || crypto_verify_pkcs12 (blob, password, NULL))
+			return FALSE;
+	} else
+		g_warning ("%s: unknown private key password scheme", __func__);
+
+	return TRUE;
+}
+
+static void
+need_secrets_tls (NMSetting8021x *self,
+                  GPtrArray *secrets,
+                  gboolean phase2)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+	NMSetting8021xCKScheme scheme;
+	const GByteArray *blob = NULL;
+	const char *path = NULL;
+
+	if (phase2) {
+		if (!priv->phase2_private_key || !priv->phase2_private_key->len) {
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+			return;
+		}
+
+		scheme = nm_setting_802_1x_get_phase2_private_key_scheme (self);
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+			path = nm_setting_802_1x_get_phase2_private_key_path (self);
+		else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+			blob = nm_setting_802_1x_get_phase2_private_key_blob (self);
+		else {
+			g_warning ("%s: unknown phase2 private key scheme %d", __func__, scheme);
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+			return;
+		}
+
+		if (need_private_key_password (blob, path, priv->phase2_private_key_password))
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
+	} else {
+		if (!priv->private_key || !priv->private_key->len) {
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY);
+			return;
+		}
+
+		scheme = nm_setting_802_1x_get_private_key_scheme (self);
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+			path = nm_setting_802_1x_get_private_key_path (self);
+		else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+			blob = nm_setting_802_1x_get_private_key_blob (self);
+		else {
+			g_warning ("%s: unknown private key scheme %d", __func__, scheme);
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY);
+			return;
+		}
+
+		if (need_private_key_password (blob, path, priv->private_key_password))
+			g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
+	}
+}
+
+static gboolean
+verify_tls (NMSetting8021x *self, gboolean phase2, GError **error)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	if (phase2) {
+		if (!priv->phase2_client_cert) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+			return FALSE;
+		} else if (!priv->phase2_client_cert->len) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+			return FALSE;
+		}
+
+		/* If the private key is PKCS#12, check that it matches the client cert */
+		if (priv->phase2_private_key && crypto_is_pkcs12_data (priv->phase2_private_key)) {
+			if (priv->phase2_private_key->len != priv->phase2_client_cert->len) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+				return FALSE;
+			}
+
+			if (memcmp (priv->phase2_private_key->data,
+			            priv->phase2_client_cert->data,
+			            priv->phase2_private_key->len)) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+				return FALSE;
+			}
+		}
+	} else {
+		if (!priv->client_cert) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_802_1X_CLIENT_CERT);
+			return FALSE;
+		} else if (!priv->client_cert->len) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_CLIENT_CERT);
+			return FALSE;
+		}
+
+		/* If the private key is PKCS#12, check that it matches the client cert */
+		if (priv->private_key && crypto_is_pkcs12_data (priv->private_key)) {
+			if (priv->private_key->len != priv->client_cert->len) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_CLIENT_CERT);
+				return FALSE;
+			}
+
+			if (memcmp (priv->private_key->data,
+			            priv->client_cert->data,
+			            priv->private_key->len)) {
+				g_set_error (error,
+				             NM_SETTING_802_1X_ERROR,
+				             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_802_1X_CLIENT_CERT);
+				return FALSE;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_ttls (NMSetting8021x *self, gboolean phase2, GError **error)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	if (   (!priv->identity || !strlen (priv->identity))
+	    && (!priv->anonymous_identity || !strlen (priv->anonymous_identity))) {
+		if (!priv->identity) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_802_1X_IDENTITY);
+		} else if (!strlen (priv->identity)) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_IDENTITY);
+		} else if (!priv->anonymous_identity) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+		} else {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+		}
+		return FALSE;
+	}
+
+	if (   (!priv->phase2_auth || !strlen (priv->phase2_auth))
+	    && (!priv->phase2_autheap || !strlen (priv->phase2_autheap))) {
+		if (!priv->phase2_auth) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_AUTH);
+		} else if (!strlen (priv->phase2_auth)) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_AUTH);
+		} else if (!priv->phase2_autheap) {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_AUTHEAP);
+		} else {
+			g_set_error (error,
+			             NM_SETTING_802_1X_ERROR,
+			             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_802_1X_PHASE2_AUTHEAP);
+		}
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_identity (NMSetting8021x *self, gboolean phase2, GError **error)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	if (!priv->identity) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_802_1X_IDENTITY);
+	} else if (!strlen (priv->identity)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_IDENTITY);
+	}
+
+	return TRUE;
+}
+
+/* Implemented below... */
+static void need_secrets_phase2 (NMSetting8021x *self,
+                                 GPtrArray *secrets,
+                                 gboolean phase2);
+
+
+typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self,
+                                          GPtrArray *secrets,
+                                          gboolean phase2);
+
+typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self,
+                                          gboolean phase2,
+                                          GError **error);
+
+typedef struct {
+	const char *method;
+	EAPMethodNeedSecretsFunc ns_func;
+	EAPMethodValidateFunc v_func;
+} EAPMethodsTable;
+
+static EAPMethodsTable eap_methods_table[] = {
+	{ "leap", need_secrets_password, verify_identity },
+	{ "md5", need_secrets_password, verify_identity },
+	{ "pap", need_secrets_password, verify_identity },
+	{ "chap", need_secrets_password, verify_identity },
+	{ "mschap", need_secrets_password, verify_identity },
+	{ "mschapv2", need_secrets_password, verify_identity },
+	{ "fast", need_secrets_password, verify_identity },
+	{ "tls", need_secrets_tls, verify_tls },
+	{ "peap", need_secrets_phase2, verify_ttls },
+	{ "ttls", need_secrets_phase2, verify_ttls },
+	{ "sim", need_secrets_sim, NULL },
+	{ "gtc", need_secrets_password, verify_identity },
+	{ "otp", NULL, NULL },  // FIXME: implement
+	{ NULL, NULL, NULL }
+};
+
+static void
+need_secrets_phase2 (NMSetting8021x *self,
+                     GPtrArray *secrets,
+                     gboolean phase2)
+{
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+	char *method = NULL;
+	int i;
+
+	g_return_if_fail (phase2 == FALSE);
+
+	/* Check phase2_auth and phase2_autheap */
+	method = priv->phase2_auth;
+	if (!method && priv->phase2_autheap)
+		method = priv->phase2_autheap;
+
+	if (!method) {
+		g_warning ("Couldn't find EAP method.");
+		g_assert_not_reached();
+		return;
+	}
+
+	/* Ask the configured phase2 method if it needs secrets */
+	for (i = 0; eap_methods_table[i].method; i++) {
+		if (eap_methods_table[i].ns_func == NULL)
+			continue;
+		if (!strcmp (eap_methods_table[i].method, method)) {
+			(*eap_methods_table[i].ns_func) (self, secrets, TRUE);
+			break;
+		}
+	}
+}
+
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+	NMSetting8021x *self = NM_SETTING_802_1X (setting);
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+	GSList *iter;
+	GPtrArray *secrets;
+	gboolean eap_method_found = FALSE;
+
+	secrets = g_ptr_array_sized_new (4);
+
+	/* Ask each configured EAP method if it needs secrets */
+	for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) {
+		const char *method = (const char *) iter->data;
+		int i;
+
+		for (i = 0; eap_methods_table[i].method; i++) {
+			if (eap_methods_table[i].ns_func == NULL)
+				continue;
+			if (!strcmp (eap_methods_table[i].method, method)) {
+				(*eap_methods_table[i].ns_func) (self, secrets, FALSE);
+
+				/* Only break out of the outer loop if this EAP method
+				 * needed secrets.
+				 */
+				if (secrets->len > 0)
+					eap_method_found = TRUE;
+				break;
+			}
+		}
+	}
+
+	if (secrets->len == 0) {
+		g_ptr_array_free (secrets, TRUE);
+		secrets = NULL;
+	}
+
+	return secrets;
+}
+
+static gboolean
+verify_cert (GByteArray *array, const char *prop_name, GError **error)
+{
+	if (!array)
+		return TRUE;
+
+	switch (get_cert_scheme (array)) {
+	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+		return TRUE;
+	case NM_SETTING_802_1X_CK_SCHEME_PATH:
+		/* For path-based schemes, verify that the path is zero-terminated */
+		if (array->data[array->len - 1] == '\0') {
+			/* And ensure it's UTF-8 valid too so we can pass it through
+			 * D-Bus and stuff like that.
+			 */
+			if (g_utf8_validate ((const char *) (array->data + strlen (SCHEME_PATH)), -1, NULL))
+				return TRUE;
+		}
+		break;
+	default:
+		break;
+	}
+
+	g_set_error (error,
+	             NM_SETTING_802_1X_ERROR,
+	             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+	             "%s", prop_name);
+	return FALSE;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSetting8021x *self = NM_SETTING_802_1X (setting);
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+	const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", NULL };
+	const char *valid_phase1_peapver[] = { "0", "1", NULL };
+	const char *valid_phase1_peaplabel[] = { "0", "1", NULL };
+	const char *valid_phase1_fast_pac[] = { "0", "1", "2", "3", NULL };
+	const char *valid_phase2_auth[] = { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL };
+	const char *valid_phase2_autheap[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL };
+	GSList *iter;
+
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	if (!priv->eap) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_802_1X_EAP);
+		return FALSE;
+	}
+
+	if (!_nm_utils_string_slist_validate (priv->eap, valid_eap)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_EAP);
+		return FALSE;
+	}
+
+	/* Ask each configured EAP method if its valid */
+	for (iter = priv->eap; iter; iter = g_slist_next (iter)) {
+		const char *method = (const char *) iter->data;
+		int i;
+
+		for (i = 0; eap_methods_table[i].method; i++) {
+			if (eap_methods_table[i].v_func == NULL)
+				continue;
+			if (!strcmp (eap_methods_table[i].method, method)) {
+				if (!(*eap_methods_table[i].v_func) (self, FALSE, error))
+					return FALSE;
+				break;
+			}
+		}
+	}
+
+	if (priv->phase1_peapver && !_nm_utils_string_in_list (priv->phase1_peapver, valid_phase1_peapver)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PHASE1_PEAPVER);
+		return FALSE;
+	}
+
+	if (priv->phase1_peaplabel && !_nm_utils_string_in_list (priv->phase1_peaplabel, valid_phase1_peaplabel)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PHASE1_PEAPLABEL);
+		return FALSE;
+	}
+
+	if (priv->phase1_fast_provisioning && !_nm_utils_string_in_list (priv->phase1_fast_provisioning, valid_phase1_fast_pac)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING);
+		return FALSE;
+	}
+
+	if (priv->phase2_auth && !_nm_utils_string_in_list (priv->phase2_auth, valid_phase2_auth)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PHASE2_AUTH);
+		return FALSE;
+	}
+
+	if (priv->phase2_autheap && !_nm_utils_string_in_list (priv->phase2_autheap, valid_phase2_autheap)) {
+		g_set_error (error,
+		             NM_SETTING_802_1X_ERROR,
+		             NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_802_1X_PHASE2_AUTHEAP);
+		return FALSE;
+	}
+
+	if (!verify_cert (priv->ca_cert, NM_SETTING_802_1X_CA_CERT, error))
+		return FALSE;
+	if (!verify_cert (priv->phase2_ca_cert, NM_SETTING_802_1X_PHASE2_CA_CERT, error))
+		return FALSE;
+
+	if (!verify_cert (priv->client_cert, NM_SETTING_802_1X_CLIENT_CERT, error))
+		return FALSE;
+	if (!verify_cert (priv->phase2_client_cert, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, error))
+		return FALSE;
+
+	if (!verify_cert (priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, error))
+		return FALSE;
+	if (!verify_cert (priv->phase2_private_key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, error))
+		return FALSE;
+
+	/* FIXME: finish */
+
+	return TRUE;
+}
+
+static void
+nm_setting_802_1x_init (NMSetting8021x *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_802_1X_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSetting8021x *self = NM_SETTING_802_1X (object);
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+	/* Strings first. g_free() already checks for NULLs so we don't have to */
+
+	g_free (priv->identity);
+	g_free (priv->anonymous_identity);
+	g_free (priv->ca_path);
+	g_free (priv->phase1_peapver);
+	g_free (priv->phase1_peaplabel);
+	g_free (priv->phase1_fast_provisioning);
+	g_free (priv->phase2_auth);
+	g_free (priv->phase2_autheap);
+	g_free (priv->phase2_ca_path);
+	g_free (priv->password);
+
+	nm_utils_slist_free (priv->eap, g_free);
+
+	if (priv->ca_cert)
+		g_byte_array_free (priv->ca_cert, TRUE);
+	if (priv->client_cert)
+		g_byte_array_free (priv->client_cert, TRUE);
+	if (priv->private_key)
+		g_byte_array_free (priv->private_key, TRUE);
+	g_free (priv->private_key_password);
+	if (priv->phase2_ca_cert)
+		g_byte_array_free (priv->phase2_ca_cert, TRUE);
+	if (priv->phase2_client_cert)
+		g_byte_array_free (priv->phase2_client_cert, TRUE);
+	if (priv->phase2_private_key)
+		g_byte_array_free (priv->phase2_private_key, TRUE);
+	g_free (priv->phase2_private_key_password);
+
+	G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object);
+}
+
+static GByteArray *
+set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error)
+{
+	gboolean valid;
+	GByteArray *data = NULL;
+
+	data = g_value_dup_boxed (value);
+	/* Verify the new data */
+	if (data) {
+		valid = verify_cert (data, prop_name, error);
+		if (!valid) {
+			g_byte_array_free (data, TRUE);
+			data = NULL;
+		}
+	}
+	return data;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSetting8021x *setting = NM_SETTING_802_1X (object);
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+	GError *error = NULL;
+
+	switch (prop_id) {
+	case PROP_EAP:
+		nm_utils_slist_free (priv->eap, g_free);
+		priv->eap = g_value_dup_boxed (value);
+		break;
+	case PROP_IDENTITY:
+		g_free (priv->identity);
+		priv->identity = g_value_dup_string (value);
+		break;
+	case PROP_ANONYMOUS_IDENTITY:
+		g_free (priv->anonymous_identity);
+		priv->anonymous_identity = g_value_dup_string (value);
+		break;
+	case PROP_CA_CERT:
+		if (priv->ca_cert) {
+			g_byte_array_free (priv->ca_cert, TRUE);
+			priv->ca_cert = NULL;
+		}
+		priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error);
+		if (error) {
+			g_warning ("Error setting certificate (invalid data): (%d) %s",
+			           error->code, error->message);
+			g_error_free (error);
+		}
+		break;
+	case PROP_CA_PATH:
+		g_free (priv->ca_path);
+		priv->ca_path = g_value_dup_string (value);
+		break;
+	case PROP_CLIENT_CERT:
+		if (priv->client_cert) {
+			g_byte_array_free (priv->client_cert, TRUE);
+			priv->client_cert = NULL;
+		}
+		priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error);
+		if (error) {
+			g_warning ("Error setting certificate (invalid data): (%d) %s",
+			           error->code, error->message);
+			g_error_free (error);
+		}
+		break;
+	case PROP_PHASE1_PEAPVER:
+		g_free (priv->phase1_peapver);
+		priv->phase1_peapver = g_value_dup_string (value);
+		break;
+	case PROP_PHASE1_PEAPLABEL:
+		g_free (priv->phase1_peaplabel);
+		priv->phase1_peaplabel = g_value_dup_string (value);
+		break;
+	case PROP_PHASE1_FAST_PROVISIONING:
+		g_free (priv->phase1_fast_provisioning);
+		priv->phase1_fast_provisioning = g_value_dup_string (value);
+		break;
+	case PROP_PHASE2_AUTH:
+		g_free (priv->phase2_auth);
+		priv->phase2_auth = g_value_dup_string (value);
+		break;
+	case PROP_PHASE2_AUTHEAP:
+		g_free (priv->phase2_autheap);
+		priv->phase2_autheap = g_value_dup_string (value);
+		break;
+	case PROP_PHASE2_CA_CERT:
+		if (priv->phase2_ca_cert) {
+			g_byte_array_free (priv->phase2_ca_cert, TRUE);
+			priv->phase2_ca_cert = NULL;
+		}
+		priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error);
+		if (error) {
+			g_warning ("Error setting certificate (invalid data): (%d) %s",
+			           error->code, error->message);
+			g_error_free (error);
+		}
+		break;
+	case PROP_PHASE2_CA_PATH:
+		g_free (priv->phase2_ca_path);
+		priv->phase2_ca_path = g_value_dup_string (value);
+		break;
+	case PROP_PHASE2_CLIENT_CERT:
+		if (priv->phase2_client_cert) {
+			g_byte_array_free (priv->phase2_client_cert, TRUE);
+			priv->phase2_client_cert = NULL;
+		}
+		priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error);
+		if (error) {
+			g_warning ("Error setting certificate (invalid data): (%d) %s",
+			           error->code, error->message);
+			g_error_free (error);
+		}
+		break;
+	case PROP_PASSWORD:
+		g_free (priv->password);
+		priv->password = g_value_dup_string (value);
+		break;
+	case PROP_PRIVATE_KEY:
+		if (priv->private_key) {
+			g_byte_array_free (priv->private_key, TRUE);
+			priv->private_key = NULL;
+		}
+		priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error);
+		if (error) {
+			g_warning ("Error setting private key (invalid data): (%d) %s",
+			           error->code, error->message);
+			g_error_free (error);
+		}
+		break;
+	case PROP_PRIVATE_KEY_PASSWORD:
+		g_free (priv->private_key_password);
+		priv->private_key_password = g_value_dup_string (value);
+		break;
+	case PROP_PHASE2_PRIVATE_KEY:
+		if (priv->phase2_private_key) {
+			g_byte_array_free (priv->phase2_private_key, TRUE);
+			priv->phase2_private_key = NULL;
+		}
+		priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error);
+		if (error) {
+			g_warning ("Error setting private key (invalid data): (%d) %s",
+			           error->code, error->message);
+			g_error_free (error);
+		}
+		break;
+	case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
+		g_free (priv->phase2_private_key_password);
+		priv->phase2_private_key_password = g_value_dup_string (value);
+		break;
+	case PROP_SYSTEM_CA_CERTS:
+		priv->system_ca_certs = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSetting8021x *setting = NM_SETTING_802_1X (object);
+	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_EAP:
+		g_value_set_boxed (value, priv->eap);
+		break;
+	case PROP_IDENTITY:
+		g_value_set_string (value, priv->identity);
+		break;
+	case PROP_ANONYMOUS_IDENTITY:
+		g_value_set_string (value, priv->anonymous_identity);
+		break;
+	case PROP_CA_CERT:
+		g_value_set_boxed (value, priv->ca_cert);
+		break;
+	case PROP_CA_PATH:
+		g_value_set_string (value, priv->ca_path);
+		break;
+	case PROP_CLIENT_CERT:
+		g_value_set_boxed (value, priv->client_cert);
+		break;
+	case PROP_PHASE1_PEAPVER:
+		g_value_set_string (value, priv->phase1_peapver);
+		break;
+	case PROP_PHASE1_PEAPLABEL:
+		g_value_set_string (value, priv->phase1_peaplabel);
+		break;
+	case PROP_PHASE1_FAST_PROVISIONING:
+		g_value_set_string (value, priv->phase1_fast_provisioning);
+		break;
+	case PROP_PHASE2_AUTH:
+		g_value_set_string (value, priv->phase2_auth);
+		break;
+	case PROP_PHASE2_AUTHEAP:
+		g_value_set_string (value, priv->phase2_autheap);
+		break;
+	case PROP_PHASE2_CA_CERT:
+		g_value_set_boxed (value, priv->phase2_ca_cert);
+		break;
+	case PROP_PHASE2_CA_PATH:
+		g_value_set_string (value, priv->phase2_ca_path);
+		break;
+	case PROP_PHASE2_CLIENT_CERT:
+		g_value_set_boxed (value, priv->phase2_client_cert);
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, priv->password);
+		break;
+	case PROP_PRIVATE_KEY:
+		g_value_set_boxed (value, priv->private_key);
+		break;
+	case PROP_PRIVATE_KEY_PASSWORD:
+		g_value_set_string (value, priv->private_key_password);
+		break;
+	case PROP_PHASE2_PRIVATE_KEY:
+		g_value_set_boxed (value, priv->phase2_private_key);
+		break;
+	case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
+		g_value_set_string (value, priv->phase2_private_key_password);
+		break;
+	case PROP_SYSTEM_CA_CERTS:
+		g_value_set_boolean (value, priv->system_ca_certs);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+	GError *error = NULL;
+
+	g_type_class_add_private (setting_class, sizeof (NMSetting8021xPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+
+	parent_class->verify         = verify;
+	parent_class->need_secrets   = need_secrets;
+
+	/* Properties */
+
+	/**
+	 * NMSetting8021x:eap:
+	 *
+	 * The allowed EAP method to be used when authenticating to the network with
+	 * 802.1x.  Valid methods are: "leap", "md5", "tls", "peap", "ttls", and
+	 * "fast".  Each method requires different configuration using the
+	 * properties of this object; refer to wpa_supplicant documentation for the
+	 * allowed combinations.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_EAP,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_EAP,
+							   "EAP",
+							   "The allowed EAP method to be used when "
+							   "authenticating to the network with 802.1x. "
+							   "Valid methods are: 'leap', 'md5', 'tls', 'peap', "
+							   "'ttls', and 'fast'. Each method requires "
+							   "different configuration using the properties of "
+							   "this setting; refer to wpa_supplicant "
+							   "documentation for the allowed combinations.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:identity:
+	 *
+	 * Identity string for EAP authentication methods.  Often the user's
+	 * user or login name.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_IDENTITY,
+		 g_param_spec_string (NM_SETTING_802_1X_IDENTITY,
+						  "Identity",
+						  "Identity string for EAP authentication methods.  "
+						  "Often the user's user or login name.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:anonymous-identity:
+	 *
+	 * Anonymous identity string for EAP authentication methods.  Used as the
+	 * unencrypted identity with EAP types that support different tunneled
+	 * identity like EAP-TTLS.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ANONYMOUS_IDENTITY,
+		 g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
+						  "Anonymous identity",
+						  "Anonymous identity string for EAP authentication "
+						  "methods.  Used as the unencrypted identity with EAP "
+						  "types that support different tunneled identity like "
+						  "EAP-TTLS.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:ca-cert:
+	 *
+	 * Contains the CA certificate if used by the EAP method specified in the
+	 * #NMSetting8021x:eap property.  Setting this property directly is
+	 * discouraged; use the nm_setting_802_1x_set_ca_cert() function instead.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_CA_CERT,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_CA_CERT,
+							   "CA certificate",
+							   "Contains the CA certificate if used by the EAP "
+							   "method specified in the 'eap' property.  "
+							   "Certificate data is specified using a 'scheme'; "
+							   "two are currently supported: blob and path.  "
+							   "When using the blob scheme (which is backwards "
+							   "compatible with NM 0.7.x) this property should "
+							   "be set to the certificate's DER encoded data.  "
+							   "When using the path scheme, this property should "
+							   "be set to the full UTF-8 encoded path of the "
+							   "certificate, prefixed with the string 'file://' "
+							   "and ending with a terminating NULL byte.  This "
+							   "property can be unset even if the EAP method "
+							   "supports CA certificates, but this allows "
+							   "man-in-the-middle attacks and is NOT recommended.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:ca-path:
+	 *
+	 * UTF-8 encoded path to a directory containing PEM or DER formatted
+	 * certificates to be added to the verification chain in addition to the
+	 * certificate specified in the #NMSetting8021x:ca-cert property.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_CA_PATH,
+		 g_param_spec_string (NM_SETTING_802_1X_CA_PATH,
+						  "CA path",
+						  "UTF-8 encoded path to a directory containing PEM or "
+						  "DER formatted certificates to be added to the "
+						  "verification chain in addition to the certificate "
+						  "specified in the 'ca-cert' property.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:client-cert:
+	 *
+	 * Contains the client certificate if used by the EAP method specified in
+	 * the #NMSetting8021x:eap property.  Setting this property directly is
+	 * discouraged; use the nm_setting_802_1x_set_client_cert() function instead.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_CLIENT_CERT,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_CLIENT_CERT,
+							   "Client certificate",
+							   "Contains the client certificate if used by the "
+							   "EAP method specified in the 'eap' property.  "
+							   "Certificate data is specified using a 'scheme'; "
+							   "two are currently supported: blob and path.  "
+							   "When using the blob scheme (which is backwards "
+							   "compatible with NM 0.7.x) this property should "
+							   "be set to the certificate's DER encoded data.  "
+							   "When using the path scheme, this property should "
+							   "be set to the full UTF-8 encoded path of the "
+							   "certificate, prefixed with the string 'file://' "
+							   "and ending with a terminating NULL byte.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase1-peapver:
+	 *
+	 * Forces which PEAP version is used when PEAP is set as the EAP method in
+	 * the #NMSetting8021x:eap property.  When unset, the version reported by
+	 * the server will be used.  Sometimes when using older RADIUS servers, it
+	 * is necessary to force the client to use a particular PEAP version.  To do
+	 * so, this property may be set to "0" or "1" to force that specific PEAP
+	 * version.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE1_PEAPVER,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER,
+						  "Phase1 PEAPVER",
+						  "Forces which PEAP version is used when PEAP is set "
+						  "as the EAP method in 'eap' property.  When unset, "
+						  "the version reported by the server will be used.  "
+						  "Sometimes when using older RADIUS servers, it is "
+						  "necessary to force the client to use a particular "
+						  "PEAP version.  To do so, this property may be set to "
+						  "'0' or '1; to force that specific PEAP version.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase1-peaplabel:
+	 *
+	 * Forces use of the new PEAP label during key derivation.  Some RADIUS
+	 * servers may require forcing the new PEAP label to interoperate with
+	 * PEAPv1.  Set to "1" to force use of the new PEAP label.  See the
+	 * wpa_supplicant documentation for more details.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE1_PEAPLABEL,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL,
+						  "Phase1 PEAP label",
+						  "Forces use of the new PEAP label during key "
+						  "derivation.  Some RADIUS servers may require forcing "
+						  "the new PEAP label to interoperate with PEAPv1.  "
+						  "Set to '1' to force use of the new PEAP label.  See "
+						  "the wpa_supplicant documentation for more details.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase1-fast-provisioning:
+	 *
+	 * Enables or disables in-line provisioning of EAP-FAST credentials when
+	 * FAST is specified as the EAP method in the #NMSetting8021x:eap property.
+	 * Recognized values are "0" (disabled), "1" (allow unauthenticated
+	 * provisioning), "2" (allow authenticated provisioning), and "3" (allow
+	 * both authenticated and unauthenticated provisioning).  See the
+	 * wpa_supplicant documentation for more details.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE1_FAST_PROVISIONING,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING,
+						  "Phase1 fast provisioning",
+						  "Enables or disables in-line provisioning of EAP-FAST "
+						  "credentials when FAST is specified as the EAP method "
+						  "in the #NMSetting8021x:eap property. Allowed values "
+						  "are '0' (disabled), '1' (allow unauthenticated "
+						  "provisioning), '2' (allow authenticated provisioning), "
+						  "and '3' (allow both authenticated and unauthenticated "
+						  "provisioning).  See the wpa_supplicant documentation "
+						  "for more details.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase2-auth:
+	 *
+	 * Specifies the allowed "phase 2" inner non-EAP authentication methods when
+	 * an EAP method that uses an inner TLS tunnel is specified in the
+	 * #NMSetting8021x:eap property.  Recognized non-EAP phase2 methods are
+	 * "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", and "tls".
+	 * Each 'phase 2' inner method requires specific parameters for successful
+	 * authentication; see the wpa_supplicant documentation for more details.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_AUTH,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH,
+						  "Phase2 auth",
+						  "Specifies the allowed 'phase 2' inner non-EAP "
+						  "authentication methods when an EAP method that uses "
+						  "an inner TLS tunnel is specified in the 'eap' "
+						  "property. Recognized non-EAP phase2 methods are 'pap', "
+						  "'chap', 'mschap', 'mschapv2', 'gtc', 'otp', 'md5', "
+						  "and 'tls'.  Each 'phase 2' inner method requires "
+						  "specific parameters for successful authentication; "
+						  "see the wpa_supplicant documentation for more details.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase2-autheap:
+	 *
+	 * Specifies the allowed "phase 2" inner EAP-based authentication methods
+	 * when an EAP method that uses an inner TLS tunnel is specified in the
+	 * #NMSetting8021x:eap property.  Recognized EAP-based phase2 methods are
+	 * "md5", "mschapv2", "otp", "gtc", and "tls". Each 'phase 2' inner method
+	 * requires specific parameters for successful authentication; see the
+	 * wpa_supplicant documentation for more details.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_AUTHEAP,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP,
+						  "Phase2 autheap",
+						  "Specifies the allowed 'phase 2' inner EAP-based "
+						  "authentication methods when an EAP method that uses "
+						  "an inner TLS tunnel is specified in the 'eap' "
+						  "property. Recognized EAP-based 'phase 2' methods are "
+						  "'md5', 'mschapv2', 'otp', 'gtc', and 'tls'. Each "
+						  "'phase 2' inner method requires specific parameters "
+						  "for successful authentication; see the wpa_supplicant "
+						  "documentation for more details.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase2-ca-cert:
+	 *
+	 * Contains the CA certificate if used by the EAP method specified in the
+	 * #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap properties.
+	 * Setting this property directly is discouraged; use the
+	 * nm_setting_802_1x_set_phase2_ca_cert() function instead.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_CA_CERT,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CA_CERT,
+							   "Phase2 CA certificate",
+							   "Contains the 'phase 2' CA certificate if used by "
+							   "the EAP method specified in the 'phase2-auth' or "
+							   "'phase2-autheap' properties.  Certificate data "
+							   "is specified using a 'scheme'; two are currently"
+							   "supported: blob and path. When using the blob "
+							   "scheme (which is backwards compatible with NM "
+							   "0.7.x) this property should be set to the "
+							   "certificate's DER encoded data. When using the "
+							   "path scheme, this property should be set to the "
+							   "full UTF-8 encoded path of the certificate, "
+							   "prefixed with the string 'file://' and ending "
+							   "with a terminating NULL byte.  This property can "
+							   "be unset even if the EAP method supports CA "
+							   "certificates, but this allows man-in-the-middle "
+							   "attacks and is NOT recommended.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase2-ca-path:
+	 *
+	 * UTF-8 encoded path to a directory containing PEM or DER formatted
+	 * certificates to be added to the verification chain in addition to the
+	 * certificate specified in the #NMSetting8021x:phase2-ca-cert property.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_CA_PATH,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH,
+						  "Phase2 auth CA path",
+						  "UTF-8 encoded path to a directory containing PEM or "
+						  "DER formatted certificates to be added to the "
+						  "verification chain in addition to the certificate "
+						  "specified in the 'phase2-ca-cert' property.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:phase2-client-cert:
+	 *
+	 * Contains the client certificate if used by the EAP method specified in
+	 * the #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap
+	 * properties.  Setting this property directly is discouraged; use the
+	 * nm_setting_802_1x_set_phase2_client_cert() function instead.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_CLIENT_CERT,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+							   "Phase2 client certificate",
+							   "Contains the 'phase 2' client certificate if "
+							   "used by the EAP method specified in the "
+							   "'phase2-eap' or 'phase2-autheap' properties. "
+							   "Certificate data is specified using a 'scheme'; "
+							   "two are currently supported: blob and path.  "
+							   "When using the blob scheme (which is backwards "
+							   "compatible with NM 0.7.x) this property should "
+							   "be set to the certificate's DER encoded data.  "
+							   "When using the path scheme, this property should "
+							   "be set to the full UTF-8 encoded path of the "
+							   "certificate, prefixed with the string 'file://' "
+							   "and ending with a terminating NULL byte.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSetting8021x:password:
+	 *
+	 * Password used for EAP authentication methods.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PASSWORD,
+		 g_param_spec_string (NM_SETTING_802_1X_PASSWORD,
+						  "Password",
+						  "Password used for EAP authentication methods.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSetting8021x:private-key:
+	 *
+	 * Contains the private key if the #NMSetting8021x:eap property is set to
+	 * 'tls'.  Setting this property directly is discouraged; use the
+	 * nm_setting_802_1x_set_private_key() function instead.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PRIVATE_KEY,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_PRIVATE_KEY,
+							   "Private key",
+							   "Contains the private key when the 'eap' property "
+							   "is set to 'tls'.  Key data is specified using a "
+							   "'scheme'; two are currently supported: blob and "
+							   "path. When using the blob scheme and X.509 private "
+							   "keys, this property should be set to the keys's "
+							   "decrypted DER encoded data.  When using X.509 "
+							   "private keys with the path scheme, this property "
+							   "should be set to the full UTF-8 encoded path of "
+							   "the key, prefixed with the string 'file://' and "
+							   "and ending with a terminating NULL byte.  When "
+							   "using PKCS#12 format private keys and the blob "
+							   "scheme, this property should be set to the "
+							   "PKCS#12 data (which is encrypted) and the "
+							   "'private-key-password' property must be set to "
+							   "password used to decrypt the PKCS#12 certificate "
+							   "and key.  When using PKCS#12 files and the path "
+							   "scheme, this property should be set to the full "
+							   "UTF-8 encoded path of the key, prefixed with the "
+							   "string 'file://' and and ending with a "
+							   "terminating NULL byte, and as with the blob "
+							   "scheme the 'private-key-password' property must "
+							   "be set to the password used to decode the PKCS#12 "
+							   "private key and certificate.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSetting8021x:private-key-password:
+	 *
+	 * The password used to decrypt the private key specified in
+	 * #NMSetting8021x:private-key when the private key either uses the path
+	 * scheme, or if the private key is a PKCS#12 format key.  Setting this
+	 * property directly is not generally necessary except when returning
+	 * secrets to NetworkManager; it is generally set automatically when setting
+	 * the private key by the nm_setting_802_1x_set_private_key() function.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PRIVATE_KEY_PASSWORD,
+		 g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
+						  "Private key password",
+						  "The password used to decrypt the private key "
+						  "specified in the 'private-key' property when the "
+						  "private key either uses the path scheme, or if the "
+						  "private key is a PKCS#12 format key.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSetting8021x:phase2-private-key:
+	 *
+	 * Private key data used by "phase 2" inner authentication methods.
+	 *
+	 * Contains the "phase 2" inner private key if the #NMSetting8021x:auth or
+	 * #NMSetting8021x:phase2-autheap property is set to 'tls'.  Setting this
+	 * property directly is discouraged; use the
+	 * nm_setting_802_1x_set_phase2_private_key() function instead.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_PRIVATE_KEY,
+		 _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+							   "Phase2 private key",
+							   "Contains the 'phase 2' inner private key when "
+							   "the 'phase2-eap' or 'phase2-autheap' property "
+							   "is set to 'tls'.  Key data is specified using a "
+							   "'scheme'; two are currently supported: blob and "
+							   "path. When using the blob scheme and X.509 private "
+							   "keys, this property should be set to the keys's "
+							   "decrypted DER encoded data.  When using X.509 "
+							   "private keys with the path scheme, this property "
+							   "should be set to the full UTF-8 encoded path of "
+							   "the key, prefixed with the string 'file://' and "
+							   "and ending with a terminating NULL byte.  When "
+							   "using PKCS#12 format private keys and the blob "
+							   "scheme, this property should be set to the "
+							   "PKCS#12 data (which is encrypted) and the "
+							   "'private-key-password' property must be set to "
+							   "password used to decrypt the PKCS#12 certificate "
+							   "and key.  When using PKCS#12 files and the path "
+							   "scheme, this property should be set to the full "
+							   "UTF-8 encoded path of the key, prefixed with the "
+							   "string 'file://' and and ending with a "
+							   "terminating NULL byte, and as with the blob "
+							   "scheme the 'private-key-password' property must "
+							   "be set to the password used to decode the PKCS#12 "
+							   "private key and certificate.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSetting8021x:phase2-private-key-password:
+	 *
+	 * The password used to decrypt the private key specified in
+	 * #NMSetting8021x:phase2-private-key when the private key either uses the
+	 * path scheme, or if the private key is a PKCS#12 format key.  Setting this
+	 * property directly is not generally necessary except when returning
+	 * secrets to NetworkManager; it is generally set automatically when setting
+	 * the private key by the nm_setting_802_1x_set_phase2_private_key() function.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD,
+		 g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
+						  "Phase2 private key password",
+						  "The password used to decrypt the 'phase 2' private "
+						  "key specified in the 'private-key' property when the "
+						  "phase2 private key either uses the path scheme, or "
+						  "if the phase2 private key is a PKCS#12 format key.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSetting8021x:system-ca-certs:
+	 *
+	 * When TRUE, overrides #NMSetting8021x:ca-path and
+	 * #NMSetting8021x:phase2-ca-path properties using the system CA directory
+	 * specified at configure time with the --system-ca-path switch.  The
+	 * certificates in this directory are added to the verification chain in
+	 * addition to any certificates specified by the #NMSetting8021x:ca-cert,
+	 * #NMSetting8021x:ca-cert-path, #NMSetting8021x:phase2-ca-cert and
+	 * #NMSetting8021x:phase2-ca-cert-path properties.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SYSTEM_CA_CERTS,
+		 g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS,
+							   "Use system CA certificates",
+							   "When TRUE, overrides 'ca-path' and 'phase2-ca-path' "
+							   "properties using the system CA directory "
+							   "specified at configure time with the "
+							   "--system-ca-path switch.  The certificates in "
+							   "this directory are added to the verification "
+							   "chain in addition to any certificates specified "
+							   "by the 'ca-cert' and 'phase2-ca-cert' properties.",
+							   FALSE,
+							   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/* Initialize crypto lbrary. */
+	if (!nm_utils_init (&error)) {
+		g_warning ("Couldn't initilize nm-utils/crypto system: %d %s",
+		           error->code, error->message);
+		g_error_free (error);
+	}
+
+}
diff --git a/libnm-util/nm-setting-8021x.h b/libnm-util/nm-setting-8021x.h
new file mode 100644
index 00000000..7ee868a0
--- /dev/null
+++ b/libnm-util/nm-setting-8021x.h
@@ -0,0 +1,280 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_8021X_H
+#define NM_SETTING_8021X_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	NM_SETTING_802_1X_CK_FORMAT_UNKNOWN = 0,
+	NM_SETTING_802_1X_CK_FORMAT_X509,
+	NM_SETTING_802_1X_CK_FORMAT_RAW_KEY,
+	NM_SETTING_802_1X_CK_FORMAT_PKCS12
+} NMSetting8021xCKFormat;
+
+typedef enum {
+	NM_SETTING_802_1X_CK_SCHEME_UNKNOWN = 0,
+	NM_SETTING_802_1X_CK_SCHEME_BLOB,
+	NM_SETTING_802_1X_CK_SCHEME_PATH
+} NMSetting8021xCKScheme;
+
+#define NM_TYPE_SETTING_802_1X            (nm_setting_802_1x_get_type ())
+#define NM_SETTING_802_1X(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021x))
+#define NM_SETTING_802_1X_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_802_1X, NMSetting8021xClass))
+#define NM_IS_SETTING_802_1X(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_802_1X))
+#define NM_IS_SETTING_802_1X_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_802_1X))
+#define NM_SETTING_802_1X_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021xClass))
+
+#define NM_SETTING_802_1X_SETTING_NAME "802-1x"
+
+typedef enum
+{
+	NM_SETTING_802_1X_ERROR_UNKNOWN = 0,
+	NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+	NM_SETTING_802_1X_ERROR_MISSING_PROPERTY
+} NMSetting8021xError;
+
+#define NM_TYPE_SETTING_802_1X_ERROR (nm_setting_802_1x_error_get_type ()) 
+GType nm_setting_802_1x_error_get_type (void);
+
+#define NM_SETTING_802_1X_ERROR nm_setting_802_1x_error_quark ()
+GQuark nm_setting_802_1x_error_quark (void);
+
+
+#define NM_SETTING_802_1X_EAP "eap"
+#define NM_SETTING_802_1X_IDENTITY "identity"
+#define NM_SETTING_802_1X_ANONYMOUS_IDENTITY "anonymous-identity"
+#define NM_SETTING_802_1X_CA_CERT "ca-cert"
+#define NM_SETTING_802_1X_CA_PATH "ca-path"
+#define NM_SETTING_802_1X_CLIENT_CERT "client-cert"
+#define NM_SETTING_802_1X_PHASE1_PEAPVER "phase1-peapver"
+#define NM_SETTING_802_1X_PHASE1_PEAPLABEL "phase1-peaplabel"
+#define NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING "phase1-fast-provisioning"
+#define NM_SETTING_802_1X_PHASE2_AUTH "phase2-auth"
+#define NM_SETTING_802_1X_PHASE2_AUTHEAP "phase2-autheap"
+#define NM_SETTING_802_1X_PHASE2_CA_CERT "phase2-ca-cert"
+#define NM_SETTING_802_1X_PHASE2_CA_PATH "phase2-ca-path"
+#define NM_SETTING_802_1X_PHASE2_CLIENT_CERT "phase2-client-cert"
+#define NM_SETTING_802_1X_PASSWORD "password"
+#define NM_SETTING_802_1X_PRIVATE_KEY "private-key"
+#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD "private-key-password"
+#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY "phase2-private-key"
+#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD "phase2-private-key-password"
+#define NM_SETTING_802_1X_PIN "pin"
+#define NM_SETTING_802_1X_PSK "psk"
+#define NM_SETTING_802_1X_SYSTEM_CA_CERTS "system-ca-certs"
+
+/* PRIVATE KEY NOTE: when setting PKCS#12 private keys directly via properties
+ * using the "blob" scheme, the data must be passed in PKCS#12 format.  In this
+ * case, the private key password must also be passed to NetworkManager, and the
+ * appropriate "client-cert" (or "phase2-client-cert") property of the
+ * NMSetting8021x object must also contain the exact same PKCS#12 data that the
+ * private key will when NetworkManager requests secrets.  This is because the
+ * PKCS#12 file contains both the private key and client certificate, so both
+ * properties need to be set to the same thing.  When using the "path" scheme,
+ * just set both the private-key and client-cert properties to the same path,
+ * and set the private-key password correctly.
+ *
+ * When setting OpenSSL-derived "traditional" format (ie S/MIME style, not
+ * PKCS#8) RSA and DSA keys directly via properties with the "blob" scheme, they
+ * must passed to NetworkManager completely decrypted because the OpenSSL
+ * "traditional" format is non-standard and is not complete enough for all
+ * crypto libraries to use.  Thus, for OpenSSL "traditional" format keys, the
+ * private key password is not passed to NetworkManager (because the data is
+ * already decrypted by the client), and the appropriate "client-cert" (or
+ * "phase2-client-cert") property of the NMSetting8021x object must be a valid
+ * client certificate.  When using the "path" scheme, just set the private-key
+ * and client-cert properties to the paths to their respective objects, and
+ * set the private-key password correctly.
+ */
+
+typedef struct {
+	NMSetting parent;
+} NMSetting8021x;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSetting8021xClass;
+
+GType nm_setting_802_1x_get_type (void);
+
+NMSetting *nm_setting_802_1x_new (void);
+
+guint32           nm_setting_802_1x_get_num_eap_methods              (NMSetting8021x *setting);
+const char *      nm_setting_802_1x_get_eap_method                   (NMSetting8021x *setting, guint32 i);
+gboolean          nm_setting_802_1x_add_eap_method                   (NMSetting8021x *setting, const char *eap);
+void              nm_setting_802_1x_remove_eap_method                (NMSetting8021x *setting, guint32 i);
+void              nm_setting_802_1x_clear_eap_methods                (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_identity                     (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_anonymous_identity           (NMSetting8021x *setting);
+
+gboolean          nm_setting_802_1x_get_system_ca_certs              (NMSetting8021x *setting);
+const char *      nm_setting_802_1x_get_ca_path                      (NMSetting8021x *setting);
+const char *      nm_setting_802_1x_get_phase2_ca_path               (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_ca_cert_scheme          (NMSetting8021x *setting);
+const GByteArray *     nm_setting_802_1x_get_ca_cert_blob            (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_ca_cert_path            (NMSetting8021x *setting);
+gboolean               nm_setting_802_1x_set_ca_cert                 (NMSetting8021x *setting,
+                                                                      const char *value,
+                                                                      NMSetting8021xCKScheme scheme,
+                                                                      NMSetting8021xCKFormat *out_format,
+                                                                      GError **error);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_client_cert_scheme      (NMSetting8021x *setting);
+const GByteArray *     nm_setting_802_1x_get_client_cert_blob        (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_client_cert_path        (NMSetting8021x *setting);
+gboolean               nm_setting_802_1x_set_client_cert             (NMSetting8021x *setting,
+                                                                      const char *value,
+                                                                      NMSetting8021xCKScheme scheme,
+                                                                      NMSetting8021xCKFormat *out_format,
+                                                                      GError **error);
+
+const char *      nm_setting_802_1x_get_phase1_peapver               (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_phase1_peaplabel             (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_phase1_fast_provisioning     (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_phase2_auth                  (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_phase2_autheap               (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_ca_cert_scheme   (NMSetting8021x *setting);
+const GByteArray *     nm_setting_802_1x_get_phase2_ca_cert_blob     (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_phase2_ca_cert_path     (NMSetting8021x *setting);
+gboolean               nm_setting_802_1x_set_phase2_ca_cert          (NMSetting8021x *setting,
+                                                                      const char *value,
+                                                                      NMSetting8021xCKScheme scheme,
+                                                                      NMSetting8021xCKFormat *out_format,
+                                                                      GError **error);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_client_cert_scheme   (NMSetting8021x *setting);
+const GByteArray *     nm_setting_802_1x_get_phase2_client_cert_blob     (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_phase2_client_cert_path     (NMSetting8021x *setting);
+gboolean               nm_setting_802_1x_set_phase2_client_cert          (NMSetting8021x *setting,
+                                                                          const char *value,
+                                                                          NMSetting8021xCKScheme scheme,
+                                                                          NMSetting8021xCKFormat *out_format,
+                                                                          GError **error);
+
+const char *      nm_setting_802_1x_get_password                     (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_pin                          (NMSetting8021x *setting);
+
+const char *      nm_setting_802_1x_get_psk                          (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_private_key_scheme          (NMSetting8021x *setting);
+const GByteArray *     nm_setting_802_1x_get_private_key_blob            (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_private_key_path            (NMSetting8021x *setting);
+gboolean               nm_setting_802_1x_set_private_key                 (NMSetting8021x *setting,
+                                                                          const char *value,
+                                                                          const char *password,
+                                                                          NMSetting8021xCKScheme scheme,
+                                                                          NMSetting8021xCKFormat *out_format,
+                                                                          GError **error);
+const char *           nm_setting_802_1x_get_private_key_password        (NMSetting8021x *setting);
+
+NMSetting8021xCKFormat nm_setting_802_1x_get_private_key_format          (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_private_key_scheme   (NMSetting8021x *setting);
+const GByteArray *     nm_setting_802_1x_get_phase2_private_key_blob     (NMSetting8021x *setting);
+const char *           nm_setting_802_1x_get_phase2_private_key_path     (NMSetting8021x *setting);
+gboolean               nm_setting_802_1x_set_phase2_private_key          (NMSetting8021x *setting,
+                                                                          const char *value,
+                                                                          const char *password,
+                                                                          NMSetting8021xCKScheme scheme,
+                                                                          NMSetting8021xCKFormat *out_format,
+                                                                          GError **error);
+const char *           nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting);
+
+NMSetting8021xCKFormat nm_setting_802_1x_get_phase2_private_key_format   (NMSetting8021x *setting);
+
+
+/***** DEPRECATED; anything below will be removed in version 0.9 *****/
+
+typedef enum {
+	NM_SETTING_802_1X_CK_TYPE_UNKNOWN = 0,
+	NM_SETTING_802_1X_CK_TYPE_X509,
+	NM_SETTING_802_1X_CK_TYPE_RAW_KEY,
+	NM_SETTING_802_1X_CK_TYPE_PKCS12
+} NMSetting8021xCKType;
+
+const GByteArray *nm_setting_802_1x_get_ca_cert                      (NMSetting8021x *setting);
+gboolean          nm_setting_802_1x_set_ca_cert_from_file            (NMSetting8021x *setting,
+                                                                      const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
+                                                                      GError **error);
+
+const GByteArray *nm_setting_802_1x_get_client_cert                  (NMSetting8021x *setting);
+gboolean          nm_setting_802_1x_set_client_cert_from_file        (NMSetting8021x *setting,
+                                                                      const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
+                                                                      GError **error);
+
+const GByteArray *nm_setting_802_1x_get_phase2_ca_cert               (NMSetting8021x *setting);
+gboolean          nm_setting_802_1x_set_phase2_ca_cert_from_file     (NMSetting8021x *setting,
+                                                                      const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
+                                                                      GError **error);
+
+const GByteArray *nm_setting_802_1x_get_phase2_client_cert           (NMSetting8021x *setting);
+gboolean          nm_setting_802_1x_set_phase2_client_cert_from_file (NMSetting8021x *setting,
+                                                                      const char *filename,
+                                                                      NMSetting8021xCKType *out_ck_type,
+                                                                      GError **error);
+
+const GByteArray *nm_setting_802_1x_get_private_key                  (NMSetting8021x *setting);
+gboolean          nm_setting_802_1x_set_private_key_from_file        (NMSetting8021x *setting,
+                                                                      const char *filename,
+                                                                      const char *password,
+                                                                      NMSetting8021xCKType *out_ck_type,
+                                                                      GError **error);
+
+NMSetting8021xCKType nm_setting_802_1x_get_private_key_type          (NMSetting8021x *setting);
+
+const GByteArray *nm_setting_802_1x_get_phase2_private_key           (NMSetting8021x *setting);
+gboolean          nm_setting_802_1x_set_phase2_private_key_from_file (NMSetting8021x *setting,
+                                                                      const char *filename,
+                                                                      const char *password,
+                                                                      NMSetting8021xCKType *out_ck_type,
+                                                                      GError **error);
+
+NMSetting8021xCKType nm_setting_802_1x_get_phase2_private_key_type   (NMSetting8021x *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_8021X_H */
diff --git a/libnm-util/nm-setting-bluetooth.c b/libnm-util/nm-setting-bluetooth.c
new file mode 100644
index 00000000..70840df9
--- /dev/null
+++ b/libnm-util/nm-setting-bluetooth.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Bastien Nocera <hadess@hadess.net>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <net/ethernet.h>
+
+#include "nm-param-spec-specialized.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-gsm.h"
+
+GQuark
+nm_setting_bluetooth_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-bluetooth-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_bluetooth_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (NM_SETTING_BLUETOOTH_ERROR_UNKNOWN, "UnknownError"),
+			ENUM_ENTRY (NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			ENUM_ENTRY (NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			ENUM_ENTRY (NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND, "TypeSettingNotFound"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingBluetoothError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING)
+
+#define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate))
+
+typedef struct {
+	GByteArray *bdaddr;
+	char *type;
+} NMSettingBluetoothPrivate;
+
+enum {
+	PROP_0,
+	PROP_BDADDR,
+	PROP_TYPE,
+
+	LAST_PROP
+};
+
+NMSetting *nm_setting_bluetooth_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BLUETOOTH, NULL);
+}
+
+const char *
+nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (setting), 0);
+
+	return NM_SETTING_BLUETOOTH_GET_PRIVATE (setting)->type;
+}
+
+const GByteArray *
+nm_setting_bluetooth_get_bdaddr (NMSettingBluetooth *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (setting), NULL);
+
+	return NM_SETTING_BLUETOOTH_GET_PRIVATE (setting)->bdaddr;
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (setting);
+
+	if (!priv->bdaddr) {
+		g_set_error (error,
+		             NM_SETTING_BLUETOOTH_ERROR,
+		             NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_BLUETOOTH_BDADDR);
+		return FALSE;
+	}
+
+	if (priv->bdaddr && priv->bdaddr->len != ETH_ALEN) {
+		g_set_error (error,
+		             NM_SETTING_BLUETOOTH_ERROR,
+		             NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_BLUETOOTH_BDADDR);
+		return FALSE;
+	}
+
+	if (!priv->type) {
+		g_set_error (error,
+		             NM_SETTING_BLUETOOTH_ERROR,
+		             NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_BLUETOOTH_TYPE);
+		return FALSE;
+	} else if (!g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN) &&
+		   !g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
+		g_set_error (error,
+		             NM_SETTING_BLUETOOTH_ERROR,
+		             NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_BLUETOOTH_TYPE);
+		return FALSE;
+	}
+
+	/* Make sure the corresponding 'type' setting is present */
+	if (   all_settings
+	    && !strcmp (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN)) {
+		gboolean gsm = FALSE, cdma = FALSE;
+
+		gsm = !!g_slist_find_custom (all_settings,
+		                             (gpointer) NM_SETTING_GSM_SETTING_NAME,
+		                             find_setting_by_name);
+		cdma = !!g_slist_find_custom (all_settings,
+		                              (gpointer) NM_SETTING_CDMA_SETTING_NAME,
+		                              find_setting_by_name);
+
+		if (!gsm && !cdma) {
+			g_set_error (error,
+			             NM_SETTING_BLUETOOTH_ERROR,
+			             NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND,
+			             NM_SETTING_BLUETOOTH_TYPE);
+			return FALSE;
+		}
+	}
+	/* PANU doesn't need a 'type' setting since no further configuration
+	 * is required at the interface level.
+	 */
+
+	return TRUE;
+}
+
+static void
+nm_setting_bluetooth_init (NMSettingBluetooth *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_BLUETOOTH_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object);
+
+	if (priv->bdaddr)
+		g_byte_array_free (priv->bdaddr, TRUE);
+
+	G_OBJECT_CLASS (nm_setting_bluetooth_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_BDADDR:
+		if (priv->bdaddr)
+			g_byte_array_free (priv->bdaddr, TRUE);
+		priv->bdaddr = g_value_dup_boxed (value);
+		break;
+	case PROP_TYPE:
+		g_free (priv->type);
+		priv->type = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingBluetooth *setting = NM_SETTING_BLUETOOTH (object);
+
+	switch (prop_id) {
+	case PROP_BDADDR:
+		g_value_set_boxed (value, nm_setting_bluetooth_get_bdaddr (setting));
+		break;
+	case PROP_TYPE:
+		g_value_set_string (value, nm_setting_bluetooth_get_connection_type (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingBluetoothPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+
+	/**
+	 * NMSettingBluetooth:bdaddr:
+	 *
+	 * The Bluetooth address of the device.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BDADDR,
+		 _nm_param_spec_specialized (NM_SETTING_BLUETOOTH_BDADDR,
+		                             "Bluetooth address",
+		                             "The Bluetooth address of the device",
+		                             DBUS_TYPE_G_UCHAR_ARRAY,
+		                             G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingBluetooth:type:
+	 *
+	 * Either 'dun' for Dial-Up Networking connections (not yet supported) or
+	 * 'panu' for Personal Area Networking connections.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_TYPE,
+		 g_param_spec_string (NM_SETTING_BLUETOOTH_TYPE,
+						  "Connection type",
+						  "Either '" NM_SETTING_BLUETOOTH_TYPE_DUN "' for "
+						  "Dial-Up Networking connections (not yet supported) "
+						  " or '" NM_SETTING_BLUETOOTH_TYPE_PANU "' for "
+						  "Personal Area Networking connections.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+}
diff --git a/libnm-util/nm-setting-bluetooth.h b/libnm-util/nm-setting-bluetooth.h
new file mode 100644
index 00000000..e41183bc
--- /dev/null
+++ b/libnm-util/nm-setting-bluetooth.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Bastien Nocera <hadess@hadess.net>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_BLUETOOTH_H
+#define NM_SETTING_BLUETOOTH_H
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_BLUETOOTH            (nm_setting_bluetooth_get_type ())
+#define NM_SETTING_BLUETOOTH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetooth))
+#define NM_SETTING_BLUETOOTH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothClass))
+#define NM_IS_SETTING_BLUETOOTH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BLUETOOTH))
+#define NM_IS_SETTING_BLUETOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_BLUETOOTH))
+#define NM_SETTING_BLUETOOTH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothClass))
+
+#define NM_SETTING_BLUETOOTH_SETTING_NAME "bluetooth"
+
+typedef enum
+{
+	NM_SETTING_BLUETOOTH_ERROR_UNKNOWN = 0,
+	NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+	NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY,
+	NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND,
+} NMSettingBluetoothError;
+
+#define NM_TYPE_SETTING_BLUETOOTH_ERROR (nm_setting_bluetooth_error_get_type ()) 
+GType nm_setting_bluetooth_error_get_type (void);
+
+#define NM_SETTING_BLUETOOTH_ERROR nm_setting_bluetooth_error_quark ()
+GQuark nm_setting_bluetooth_error_quark (void);
+
+#define NM_SETTING_BLUETOOTH_BDADDR    "bdaddr"
+#define NM_SETTING_BLUETOOTH_TYPE      "type"
+
+#define NM_SETTING_BLUETOOTH_TYPE_DUN  "dun"
+#define NM_SETTING_BLUETOOTH_TYPE_PANU "panu"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingBluetooth;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingBluetoothClass;
+
+GType nm_setting_bluetooth_get_type (void);
+
+NMSetting *       nm_setting_bluetooth_new                 (void);
+const GByteArray *nm_setting_bluetooth_get_bdaddr          (NMSettingBluetooth *setting);
+const char *      nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_BLUETOOTH_H */
diff --git a/libnm-util/nm-setting-cdma.c b/libnm-util/nm-setting-cdma.c
new file mode 100644
index 00000000..7255eb91
--- /dev/null
+++ b/libnm-util/nm-setting-cdma.c
@@ -0,0 +1,363 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include "nm-setting-cdma.h"
+#include "nm-setting-serial.h"
+#include "nm-utils.h"
+
+/**
+ * SECTION:nm-setting-cdma
+ * @short_description: Describes CDMA-based mobile broadband properties
+ * @include: nm-setting-cdma.h
+ *
+ * The #NMSettingCdma object is a #NMSetting subclass that describes
+ * properties that allow connections to IS-95-based mobile broadband
+ * networks, including those using CDMA2000/EVDO technology.
+ */
+
+/**
+ * nm_setting_cdma_error_quark:
+ *
+ * Registers an error quark for #NMSettingCdma if necessary.
+ *
+ * Returns: the error quark used for #NMSettingCdma errors.
+ **/
+GQuark
+nm_setting_cdma_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-cdma-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_cdma_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_CDMA_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_CDMA_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_CDMA_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The required serial setting is missing */
+			ENUM_ENTRY (NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING, "MissingSerialSetting"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingCdmaError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING)
+
+#define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate))
+
+typedef struct {
+	char *number; /* For dialing, duh */
+	char *username;
+	char *password;
+} NMSettingCdmaPrivate;
+
+enum {
+	PROP_0,
+	PROP_NUMBER,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+
+	LAST_PROP
+};
+
+/**
+ * nm_setting_cdma_new:
+ *
+ * Creates a new #NMSettingCdma object with default values.
+ *
+ * Returns: the new empty #NMSettingCdma object
+ **/
+NMSetting *
+nm_setting_cdma_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_CDMA, NULL);
+}
+
+/**
+ * nm_setting_cdma_get_number:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingCdma:number property of the setting
+ **/
+const char *
+nm_setting_cdma_get_number (NMSettingCdma *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL);
+
+	return NM_SETTING_CDMA_GET_PRIVATE (setting)->number;
+}
+
+/**
+ * nm_setting_cdma_get_username:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingCdma:username property of the setting
+ **/
+const char *
+nm_setting_cdma_get_username (NMSettingCdma *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL);
+
+	return NM_SETTING_CDMA_GET_PRIVATE (setting)->username;
+}
+
+/**
+ * nm_setting_cdma_get_password:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingCdma:password property of the setting
+ **/
+const char *
+nm_setting_cdma_get_password (NMSettingCdma *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL);
+
+	return NM_SETTING_CDMA_GET_PRIVATE (setting)->password;
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (setting);
+
+	/* Serial connections require a PPP setting */
+	if (all_settings && 
+	    !g_slist_find_custom (all_settings, NM_SETTING_SERIAL_SETTING_NAME, find_setting_by_name)) {
+		g_set_error (error,
+		             NM_SETTING_CDMA_ERROR,
+		             NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING,
+		             NULL);
+		return FALSE;
+	}
+
+	if (!priv->number) {
+		g_set_error (error,
+		             NM_SETTING_CDMA_ERROR,
+		             NM_SETTING_CDMA_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_CDMA_NUMBER);
+		return FALSE;
+	} else if (!strlen (priv->number)) {
+		g_set_error (error,
+		             NM_SETTING_CDMA_ERROR,
+		             NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_CDMA_NUMBER);
+		return FALSE;
+	}
+
+	if (priv->username && !strlen (priv->username)) {
+		g_set_error (error,
+		             NM_SETTING_CDMA_ERROR,
+		             NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_CDMA_USERNAME);
+		return FALSE;
+	}
+
+	if (priv->password && !strlen (priv->password)) {
+		g_set_error (error,
+		             NM_SETTING_CDMA_ERROR,
+		             NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_CDMA_PASSWORD);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+	NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (setting);
+	GPtrArray *secrets = NULL;
+
+	if (priv->password)
+		return NULL;
+
+	if (priv->username) {
+		secrets = g_ptr_array_sized_new (1);
+		g_ptr_array_add (secrets, NM_SETTING_CDMA_PASSWORD);
+	}
+
+	return secrets;
+}
+
+static void
+nm_setting_cdma_init (NMSettingCdma *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_CDMA_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object);
+
+	g_free (priv->number);
+	g_free (priv->username);
+	g_free (priv->password);
+
+	G_OBJECT_CLASS (nm_setting_cdma_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_NUMBER:
+		g_free (priv->number);
+		priv->number = g_value_dup_string (value);
+		break;
+	case PROP_USERNAME:
+		g_free (priv->username);
+		priv->username = g_value_dup_string (value);
+		break;
+	case PROP_PASSWORD:
+		g_free (priv->password);
+		priv->password = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingCdma *setting = NM_SETTING_CDMA (object);
+
+	switch (prop_id) {
+	case PROP_NUMBER:
+		g_value_set_string (value, nm_setting_cdma_get_number (setting));
+		break;
+	case PROP_USERNAME:
+		g_value_set_string (value, nm_setting_cdma_get_username (setting));
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nm_setting_cdma_get_password (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_cdma_class_init (NMSettingCdmaClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingCdmaPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+	parent_class->need_secrets = need_secrets;
+
+	/* Properties */
+
+	/**
+	 * NMSettingCdma:number:
+	 *
+	 * The number to dial to establish the connection to the CDMA-based mobile
+	 * broadband network, if any.  If not specified, the default number (#777)
+	 * is used when required.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NUMBER,
+		 g_param_spec_string (NM_SETTING_CDMA_NUMBER,
+						  "Number",
+						  "Number to dial when establishing a PPP data session "
+						  "with the CDMA-based mobile broadband network.  If not "
+						  "specified, the default number (#777) is used when "
+						  "required.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingCdma:username:
+	 *
+	 * The username used to authenticate with the network, if required.  Many
+	 * providers do not require a username, or accept any username.  But if a
+	 * username is required, it is specified here.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_USERNAME,
+		 g_param_spec_string (NM_SETTING_CDMA_USERNAME,
+						  "Username",
+						  "Username used to authenticate with the network, if "
+						  "required.  Note that many providers do not require "
+						  "a username or accept any username.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingCdma:password:
+	 *
+	 * The password used to authenticate with the network, if required.  Many
+	 * providers do not require a password, or accept any password.  But if a
+	 * password is required, it is specified here.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PASSWORD,
+		 g_param_spec_string (NM_SETTING_CDMA_PASSWORD,
+						  "Password",
+						  "Password used to authenticate with the network, if "
+						  "required.  Note that many providers do not require "
+						  "a password or accept any password.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+}
diff --git a/libnm-util/nm-setting-cdma.h b/libnm-util/nm-setting-cdma.h
new file mode 100644
index 00000000..8a6c4505
--- /dev/null
+++ b/libnm-util/nm-setting-cdma.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_CDMA_H
+#define NM_SETTING_CDMA_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_CDMA            (nm_setting_cdma_get_type ())
+#define NM_SETTING_CDMA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_CDMA, NMSettingCdma))
+#define NM_SETTING_CDMA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_CDMA, NMSettingCdmaClass))
+#define NM_IS_SETTING_CDMA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_CDMA))
+#define NM_IS_SETTING_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_CDMA))
+#define NM_SETTING_CDMA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_CDMA, NMSettingCdmaClass))
+
+#define NM_SETTING_CDMA_SETTING_NAME "cdma"
+
+typedef enum
+{
+	NM_SETTING_CDMA_ERROR_UNKNOWN = 0,
+	NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+	NM_SETTING_CDMA_ERROR_MISSING_PROPERTY,
+	NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING
+} NMSettingCdmaError;
+
+#define NM_TYPE_SETTING_CDMA_ERROR (nm_setting_cdma_error_get_type ()) 
+GType nm_setting_cdma_error_get_type (void);
+
+#define NM_SETTING_CDMA_ERROR nm_setting_cdma_error_quark ()
+GQuark nm_setting_cdma_error_quark (void);
+
+#define NM_SETTING_CDMA_NUMBER       "number"
+#define NM_SETTING_CDMA_USERNAME     "username"
+#define NM_SETTING_CDMA_PASSWORD     "password"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingCdma;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingCdmaClass;
+
+GType nm_setting_cdma_get_type (void);
+
+NMSetting  *nm_setting_cdma_new          (void);
+const char *nm_setting_cdma_get_number   (NMSettingCdma *setting);
+const char *nm_setting_cdma_get_username (NMSettingCdma *setting);
+const char *nm_setting_cdma_get_password (NMSettingCdma *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_CDMA_H */
diff --git a/libnm-util/nm-setting-connection.c b/libnm-util/nm-setting-connection.c
new file mode 100644
index 00000000..65f613eb
--- /dev/null
+++ b/libnm-util/nm-setting-connection.c
@@ -0,0 +1,521 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include "nm-setting-connection.h"
+
+/**
+ * SECTION:nm-setting-connection
+ * @short_description: Describes general connection properties
+ * @include: nm-setting-connection.h
+ *
+ * The #NMSettingConnection object is a #NMSetting subclass that describes
+ * properties that apply to all #NMConnection objects, regardless of what type
+ * of network connection they describe.  Each #NMConnection object must contain
+ * a #NMSettingConnection setting.
+ **/
+
+/**
+ * nm_setting_connection_error_quark:
+ *
+ * Registers an error quark for #NMSettingConnection if necessary.
+ *
+ * Returns: the error quark used for #NMSettingConnection errors.
+ **/
+GQuark
+nm_setting_connection_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-connection-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_connection_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (NM_SETTING_CONNECTION_ERROR_UNKNOWN, "UnknownError"),
+			ENUM_ENTRY (NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			ENUM_ENTRY (NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			ENUM_ENTRY (NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND, "TypeSettingNotFound"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingConnectionError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING)
+
+#define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate))
+
+typedef struct {
+	char *id;
+	char *uuid;
+	char *type;
+	gboolean autoconnect;
+	guint64 timestamp;
+	gboolean read_only;
+} NMSettingConnectionPrivate;
+
+enum {
+	PROP_0,
+	PROP_ID,
+	PROP_UUID,
+	PROP_TYPE,
+	PROP_AUTOCONNECT,
+	PROP_TIMESTAMP,
+	PROP_READ_ONLY,
+
+	LAST_PROP
+};
+
+/**
+ * nm_setting_connection_new:
+ *
+ * Creates a new #NMSettingConnection object with default values.
+ *
+ * Returns: the new empty #NMSettingConnection object
+ **/
+NMSetting *nm_setting_connection_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_CONNECTION, NULL);
+}
+
+/**
+ * nm_setting_connection_get_id:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:id property of the connection.
+ *
+ * Returns: the connection ID
+ **/
+const char *
+nm_setting_connection_get_id (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->id;
+}
+
+/**
+ * nm_setting_connection_get_uuid:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:uuid property of the connection.
+ *
+ * Returns: the connection UUID
+ **/
+const char *
+nm_setting_connection_get_uuid (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->uuid;
+}
+
+/**
+ * nm_setting_connection_get_connection_type:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:type property of the connection.
+ *
+ * Returns: the connection type
+ **/
+const char *
+nm_setting_connection_get_connection_type (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->type;
+}
+
+/**
+ * nm_setting_connection_get_autoconnect:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:autoconnect property of the connection.
+ *
+ * Returns: the connection's autoconnect behavior
+ **/
+gboolean
+nm_setting_connection_get_autoconnect (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->autoconnect;
+}
+
+/**
+ * nm_setting_connection_get_timestamp:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:timestamp property of the connection.
+ *
+ * Returns: the connection's timestamp
+ **/
+guint64
+nm_setting_connection_get_timestamp (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->timestamp;
+}
+
+/**
+ * nm_setting_connection_get_read_only:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:read-only property of the connection.
+ *
+ * Returns: %TRUE if the connection is read-only, %FALSE if it is not
+ **/
+gboolean
+nm_setting_connection_get_read_only (NMSettingConnection *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), TRUE);
+
+	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->read_only;
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+static gboolean
+validate_uuid (const char *uuid)
+{
+	int i;
+
+	if (!uuid || !strlen (uuid))
+		return FALSE;
+
+	for (i = 0; i < strlen (uuid); i++) {
+		if (!isxdigit (uuid[i]) && (uuid[i] != '-'))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+
+	if (!priv->id) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_CONNECTION_ID);
+		return FALSE;
+	} else if (!strlen (priv->id)) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_CONNECTION_ID);
+		return FALSE;
+	}
+
+	if (!priv->uuid) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_CONNECTION_UUID);
+		return FALSE;
+	} else if (!validate_uuid (priv->uuid)) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_CONNECTION_UUID);
+		return FALSE;
+	}
+
+	if (!priv->type) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_CONNECTION_TYPE);
+		return FALSE;
+	} else if (!strlen (priv->type)) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_CONNECTION_TYPE);
+		return FALSE;
+	}
+
+	/* Make sure the corresponding 'type' item is present */
+	if (all_settings && !g_slist_find_custom (all_settings, priv->type, find_setting_by_name)) {
+		g_set_error (error,
+		             NM_SETTING_CONNECTION_ERROR,
+		             NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND,
+		             NM_SETTING_CONNECTION_TYPE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_connection_init (NMSettingConnection *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_CONNECTION_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
+
+	g_free (priv->id);
+	g_free (priv->uuid);
+	g_free (priv->type);
+
+	G_OBJECT_CLASS (nm_setting_connection_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ID:
+		g_free (priv->id);
+		priv->id = g_value_dup_string (value);
+		break;
+	case PROP_UUID:
+		g_free (priv->uuid);
+		priv->uuid = g_value_dup_string (value);
+		break;
+	case PROP_TYPE:
+		g_free (priv->type);
+		priv->type = g_value_dup_string (value);
+		break;
+	case PROP_AUTOCONNECT:
+		priv->autoconnect = g_value_get_boolean (value);
+		break;
+	case PROP_TIMESTAMP:
+		priv->timestamp = g_value_get_uint64 (value);
+		break;
+	case PROP_READ_ONLY:
+		priv->read_only = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingConnection *setting = NM_SETTING_CONNECTION (object);
+
+	switch (prop_id) {
+	case PROP_ID:
+		g_value_set_string (value, nm_setting_connection_get_id (setting));
+		break;
+	case PROP_UUID:
+		g_value_set_string (value, nm_setting_connection_get_uuid (setting));
+		break;
+	case PROP_TYPE:
+		g_value_set_string (value, nm_setting_connection_get_connection_type (setting));
+		break;
+	case PROP_AUTOCONNECT:
+		g_value_set_boolean (value, nm_setting_connection_get_autoconnect (setting));
+		break;
+	case PROP_TIMESTAMP:
+		g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting));
+		break;
+	case PROP_READ_ONLY:
+		g_value_set_boolean (value, nm_setting_connection_get_read_only (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingConnectionPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+
+	/**
+	 * NMSettingConnection:id:
+	 *
+	 * A human readable unique idenfier for the connection, like "Work WiFi" or
+	 * "T-Mobile 3G".
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ID,
+		 g_param_spec_string (NM_SETTING_CONNECTION_ID,
+						  "ID",
+						  "User-readable connection identifier/name.  Must be "
+						  "one or more characters and may change over the lifetime "
+						  "of the connection if the user decides to rename it.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingConnection:uuid:
+	 *
+	 * A universally unique idenfier for the connection, for example generated
+	 * with libuuid.  Should be assigned when the connection is created, and
+	 * never changed as long as the connection still applies to the same
+	 * network.  For example, should not be changed when the
+	 * #NMSettingConnection:id or #NMSettingIP4Config changes, but might need
+	 * to be re-created when the WiFi SSID, mobile broadband network provider,
+	 * or #NMSettingConnection:type changes.
+	 *
+	 * The UUID must be in the format '2815492f-7e56-435e-b2e9-246bd7cdc664'
+	 * (ie, contains only hexadecimal characters and '-').  A suitable UUID may
+	 * be generated by nm_utils_uuid_generate() or
+	 * nm_utils_uuid_generate_from_string().
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_UUID,
+		 g_param_spec_string (NM_SETTING_CONNECTION_UUID,
+						  "UUID",
+						  "Universally unique connection identifier.  Must be "
+						  "in the format '2815492f-7e56-435e-b2e9-246bd7cdc664' "
+						  "(ie, contains only hexadecimal characters and '-'). "
+						  "The UUID should be assigned when the connection is "
+						  "created and never changed as long as the connection "
+						  "still applies to the same network.  For example, "
+						  "it should not be changed when the user changes the "
+						  "connection's 'id', but should be recreated when the "
+						  "WiFi SSID, mobile broadband network provider, or the "
+						  "connection type changes.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingConnection:type:
+	 *
+	 * The general hardware type of the device used for the network connection,
+	 * contains the name of the #NMSetting object that describes that hardware
+	 * type's parameters.  For example, for WiFi devices, the name of the
+	 * #NMSettingWireless setting.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_TYPE,
+		 g_param_spec_string (NM_SETTING_CONNECTION_TYPE,
+						  "Type",
+						  "Base type of the connection.  For hardware-dependent "
+						  "connections, should contain the setting name of the "
+						  "hardware-type specific setting (ie, '802-3-ethernet' "
+						  "or '802-11-wireless' or 'bluetooth', etc), and for "
+						  "non-hardware dependent connections like VPN or "
+						  "otherwise, should contain the setting name of that "
+						  "setting type (ie, 'vpn' or 'bridge', etc).",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingConnection:autoconnect:
+	 *
+	 * Whether or not the connection should be automatically connected by
+	 * NetworkManager when the resources for the connection are available.
+	 * %TRUE to automatically activate the connection, %FALSE to require manual
+	 * intervention to activate the connection.  Defaults to %TRUE.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_AUTOCONNECT,
+		 g_param_spec_boolean (NM_SETTING_CONNECTION_AUTOCONNECT,
+						   "Autoconnect",
+						   "If TRUE, NetworkManager will activate this connection "
+						   "when its network resources are available.  If FALSE, "
+						   "the connection must be manually activated by the user "
+						   "or some other mechanism.",
+						   TRUE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingConnection:timestamp:
+	 *
+	 * The time, in seconds since the Unix Epoch, that the connection was last
+	 * _successfully_ fully activated.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_TIMESTAMP,
+		 g_param_spec_uint64 (NM_SETTING_CONNECTION_TIMESTAMP,
+						  "Timestamp",
+						  "Timestamp (in seconds since the Unix Epoch) that the "
+						  "connection was last successfully activated.  Settings "
+						  "services should update the connection timestamp "
+						  "periodically when the connection is active to ensure "
+						  "that an active connection has the latest timestamp.",
+						  0, G_MAXUINT64, 0,
+						  G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingConnection:read-only:
+	 *
+	 * %TRUE if the connection can be modified using the providing settings
+	 * service's D-Bus interface with the right privileges, or %FALSE
+	 * if the connection is read-only and cannot be modified.
+	 **/
+	g_object_class_install_property
+	    (object_class, PROP_READ_ONLY,
+	     g_param_spec_boolean (NM_SETTING_CONNECTION_READ_ONLY,
+	                      "Read-Only",
+	                      "If TRUE, the connection is read-only and cannot be "
+	                      "changed by the user or any other mechanism.  This is "
+	                      "normally set for system connections whose plugin "
+	                      "cannot yet write updated connections back out.",
+	                      FALSE,
+	                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+}
diff --git a/libnm-util/nm-setting-connection.h b/libnm-util/nm-setting-connection.h
new file mode 100644
index 00000000..6dedca22
--- /dev/null
+++ b/libnm-util/nm-setting-connection.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_CONNECTION_H
+#define NM_SETTING_CONNECTION_H
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_CONNECTION            (nm_setting_connection_get_type ())
+#define NM_SETTING_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_CONNECTION, NMSettingConnection))
+#define NM_SETTING_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionClass))
+#define NM_IS_SETTING_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_CONNECTION))
+#define NM_IS_SETTING_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_CONNECTION))
+#define NM_SETTING_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionClass))
+
+#define NM_SETTING_CONNECTION_SETTING_NAME "connection"
+
+/**
+ * NMSettingConnectionError:
+ * @NM_SETTING_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY: the property's value is
+ *   invalid
+ * @NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY: a required property is not
+ *   present
+ * @NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND: the #NMSetting object
+ *   referenced by the setting name contained in the
+ *   #NMSettingConnection:type property was not present in the #NMConnection
+ *
+ * Describes errors that may result from operations involving a
+ * #NMSettingConnection.
+ *
+ **/
+typedef enum
+{
+	NM_SETTING_CONNECTION_ERROR_UNKNOWN = 0,
+	NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+	NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+	NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND
+} NMSettingConnectionError;
+
+#define NM_TYPE_SETTING_CONNECTION_ERROR (nm_setting_connection_error_get_type ()) 
+GType nm_setting_connection_error_get_type (void);
+
+#define NM_SETTING_CONNECTION_ERROR nm_setting_connection_error_quark ()
+GQuark nm_setting_connection_error_quark (void);
+
+#define NM_SETTING_CONNECTION_ID          "id"
+#define NM_SETTING_CONNECTION_UUID        "uuid"
+#define NM_SETTING_CONNECTION_TYPE        "type"
+#define NM_SETTING_CONNECTION_AUTOCONNECT "autoconnect"
+#define NM_SETTING_CONNECTION_TIMESTAMP   "timestamp"
+#define NM_SETTING_CONNECTION_READ_ONLY   "read-only"
+
+/**
+ * NMSettingConnection:
+ *
+ * The NMSettingConnection struct contains only private data.
+ * It should only be accessed through the functions described below.
+ */
+typedef struct {
+	NMSetting parent;
+} NMSettingConnection;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingConnectionClass;
+
+GType nm_setting_connection_get_type (void);
+
+NMSetting * nm_setting_connection_new                 (void);
+const char *nm_setting_connection_get_id              (NMSettingConnection *setting);
+const char *nm_setting_connection_get_uuid            (NMSettingConnection *setting);
+const char *nm_setting_connection_get_connection_type (NMSettingConnection *setting);
+gboolean    nm_setting_connection_get_autoconnect     (NMSettingConnection *setting);
+guint64     nm_setting_connection_get_timestamp       (NMSettingConnection *setting);
+gboolean    nm_setting_connection_get_read_only       (NMSettingConnection *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_CONNECTION_H */
diff --git a/libnm-util/nm-setting-gsm.c b/libnm-util/nm-setting-gsm.c
new file mode 100644
index 00000000..4b4560cd
--- /dev/null
+++ b/libnm-util/nm-setting-gsm.c
@@ -0,0 +1,656 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include "nm-setting-gsm.h"
+#include "nm-setting-serial.h"
+#include "nm-utils.h"
+
+GQuark
+nm_setting_gsm_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-gsm-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_gsm_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_GSM_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_GSM_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_GSM_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The required serial setting is missing */
+			ENUM_ENTRY (NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING, "MissingSerialSetting"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingGsmError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING)
+
+#define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
+
+typedef struct {
+	char *number; /* For dialing, duh */
+	char *username;
+	char *password;
+
+	char *apn; /* NULL for dynamic */
+	char *network_id; /* for manual registration or NULL for automatic */
+	int network_type; /* One of the NM_SETTING_GSM_NETWORK_TYPE_* */
+	guint32 allowed_bands;     /* A bitfield of NM_SETTING_GSM_BAND_* */
+
+	char *pin;
+
+	gboolean home_only;
+} NMSettingGsmPrivate;
+
+enum {
+	PROP_0,
+	PROP_NUMBER,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+	PROP_APN,
+	PROP_NETWORK_ID,
+	PROP_NETWORK_TYPE,
+	PROP_BAND,
+	PROP_PIN,
+	PROP_PUK,
+	PROP_ALLOWED_BANDS,
+	PROP_HOME_ONLY,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_gsm_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_GSM, NULL);
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+const char *
+nm_setting_gsm_get_number (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->number;
+}
+
+const char *
+nm_setting_gsm_get_username (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->username;
+}
+
+const char *
+nm_setting_gsm_get_password (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->password;
+}
+
+const char *
+nm_setting_gsm_get_apn (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->apn;
+}
+
+const char *
+nm_setting_gsm_get_network_id (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->network_id;
+}
+
+int
+nm_setting_gsm_get_network_type (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), -1);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->network_type;
+}
+
+int
+nm_setting_gsm_get_band (NMSettingGsm *setting)
+{
+	g_warning ("Tried to get deprecated property " NM_SETTING_GSM_SETTING_NAME "/" NM_SETTING_GSM_BAND);
+	return -1;
+}
+
+guint32
+nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_GSM_BAND_UNKNOWN);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->allowed_bands;
+}
+
+const char *
+nm_setting_gsm_get_pin (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->pin;
+}
+
+const char *
+nm_setting_gsm_get_puk (NMSettingGsm *setting)
+{
+	g_warning ("Tried to get deprecated property " NM_SETTING_GSM_SETTING_NAME "/" NM_SETTING_GSM_PUK);
+	return NULL;
+}
+
+gboolean
+nm_setting_gsm_get_home_only (NMSettingGsm *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_GSM (setting), FALSE);
+
+	return NM_SETTING_GSM_GET_PRIVATE (setting)->home_only;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting);
+
+	/* Serial connections require a PPP setting */
+	if (all_settings && 
+	    !g_slist_find_custom (all_settings, NM_SETTING_SERIAL_SETTING_NAME, find_setting_by_name)) {
+		g_set_error (error,
+		             NM_SETTING_GSM_ERROR,
+		             NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING,
+		             NULL);
+		return FALSE;
+	}
+
+	if (!priv->number) {
+		g_set_error (error,
+		             NM_SETTING_GSM_ERROR,
+		             NM_SETTING_GSM_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_GSM_NUMBER);
+		return FALSE;
+	} else if (!strlen (priv->number)) {
+		g_set_error (error,
+		             NM_SETTING_GSM_ERROR,
+		             NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_GSM_NUMBER);
+		return FALSE;
+	}
+
+	if (priv->apn && (strlen (priv->apn) < 1 || strchr (priv->apn, '"'))) {
+		g_set_error (error,
+		             NM_SETTING_GSM_ERROR,
+		             NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_GSM_APN);
+		return FALSE;
+	}
+
+	if (priv->username && !strlen (priv->username)) {
+		g_set_error (error,
+		             NM_SETTING_GSM_ERROR,
+		             NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_GSM_USERNAME);
+		return FALSE;
+	}
+
+	if (priv->password && !strlen (priv->password)) {
+		g_set_error (error,
+		             NM_SETTING_GSM_ERROR,
+		             NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_GSM_USERNAME);
+		return FALSE;
+	}
+
+	if (priv->network_id) {
+		guint32 nid_len = strlen (priv->network_id);
+		guint32 i;
+
+		/* Accept both 5 and 6 digit MCC/MNC codes */
+		if ((nid_len < 5) || (nid_len > 6)) {
+			g_set_error (error,
+			             NM_SETTING_GSM_ERROR,
+			             NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_GSM_NETWORK_ID);
+			return FALSE;
+		}
+
+		for (i = 0; i < nid_len; i++) {
+			if (!isdigit (priv->network_id[i])) {
+				g_set_error (error,
+				             NM_SETTING_GSM_ERROR,
+				             NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_GSM_NETWORK_ID);
+				return FALSE;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+	NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting);
+	GPtrArray *secrets = NULL;
+
+	if (priv->password)
+		return NULL;
+
+	if (priv->username) {
+		secrets = g_ptr_array_sized_new (1);
+		g_ptr_array_add (secrets, NM_SETTING_GSM_PASSWORD);
+	}
+
+	return secrets;
+}
+
+static void
+nm_setting_gsm_init (NMSettingGsm *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_GSM_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
+
+	g_free (priv->number);
+	g_free (priv->username);
+	g_free (priv->password);
+	g_free (priv->apn);
+	g_free (priv->network_id);
+	g_free (priv->pin);
+
+	G_OBJECT_CLASS (nm_setting_gsm_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
+	const char *str;
+
+	switch (prop_id) {
+	case PROP_NUMBER:
+		g_free (priv->number);
+		priv->number = g_value_dup_string (value);
+		break;
+	case PROP_USERNAME:
+		g_free (priv->username);
+		priv->username = g_value_dup_string (value);
+		break;
+	case PROP_PASSWORD:
+		g_free (priv->password);
+		priv->password = g_value_dup_string (value);
+		break;
+	case PROP_APN:
+		g_free (priv->apn);
+		priv->apn = g_value_dup_string (value);
+		break;
+	case PROP_NETWORK_ID:
+		g_free (priv->network_id);
+		priv->network_id = g_value_dup_string (value);
+		break;
+	case PROP_NETWORK_TYPE:
+		priv->network_type = g_value_get_int (value);
+		break;
+	case PROP_BAND:
+		if (g_value_get_int (value) != -1)
+			g_warning ("Tried to set deprecated property " NM_SETTING_GSM_SETTING_NAME "/" NM_SETTING_GSM_BAND);
+		break;
+	case PROP_ALLOWED_BANDS:
+		priv->allowed_bands = g_value_get_uint (value);
+		break;
+	case PROP_PIN:
+		g_free (priv->pin);
+		priv->pin = g_value_dup_string (value);
+		break;
+	case PROP_PUK:
+		str = g_value_get_string (value);
+		if (str && strlen (str))
+			g_warning ("Tried to set deprecated property " NM_SETTING_GSM_SETTING_NAME "/" NM_SETTING_GSM_PUK);
+		break;
+	case PROP_HOME_ONLY:
+		priv->home_only = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingGsm *setting = NM_SETTING_GSM (object);
+
+	switch (prop_id) {
+	case PROP_NUMBER:
+		g_value_set_string (value, nm_setting_gsm_get_number (setting));
+		break;
+	case PROP_USERNAME:
+		g_value_set_string (value, nm_setting_gsm_get_username (setting));
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nm_setting_gsm_get_password (setting));
+		break;
+	case PROP_APN:
+		g_value_set_string (value, nm_setting_gsm_get_apn (setting));
+		break;
+	case PROP_NETWORK_ID:
+		g_value_set_string (value, nm_setting_gsm_get_network_id (setting));
+		break;
+	case PROP_NETWORK_TYPE:
+		g_value_set_int (value, nm_setting_gsm_get_network_type (setting));
+		break;
+	case PROP_ALLOWED_BANDS:
+		g_value_set_uint (value, nm_setting_gsm_get_allowed_bands (setting));
+		break;
+	case PROP_PIN:
+		g_value_set_string (value, nm_setting_gsm_get_pin (setting));
+		break;
+	case PROP_PUK:
+		/* deprecated */
+		g_value_set_string (value, NULL);
+		break;
+	case PROP_BAND:
+		/* deprecated */
+		g_value_set_int (value, -1);
+		break;
+	case PROP_HOME_ONLY:
+		g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_gsm_class_init (NMSettingGsmClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingGsmPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+	parent_class->need_secrets = need_secrets;
+
+	/* Properties */
+
+	/**
+	 * NMSettingGsm:number:
+	 *
+	 * Number to dial when establishing a PPP data session with the GSM-based
+	 * mobile broadband network.  In most cases, leave the number blank and a
+	 * number selecting the APN specified in the 'apn' property will be used
+	 * automatically when required.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NUMBER,
+		 g_param_spec_string (NM_SETTING_GSM_NUMBER,
+						  "Number",
+						  "Number to dial when establishing a PPP data session "
+						  "with the GSM-based mobile broadband network.  In most "
+						  "cases, leave the number blank and a number selecting "
+						  "the APN specified in the 'apn' property will be used "
+						  "automatically when required.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingGsm:username:
+	 *
+	 * The username used to authenticate with the network, if required.  Many
+	 * providers do not require a username, or accept any username.  But if a
+	 * username is required, it is specified here.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_USERNAME,
+		 g_param_spec_string (NM_SETTING_GSM_USERNAME,
+						  "Username",
+						  "Username used to authenticate with the network, if "
+						  "required.  Note that many providers do not require "
+						  "a username or accept any username.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingGsm:password:
+	 *
+	 * The password used to authenticate with the network, if required.  Many
+	 * providers do not require a password, or accept any password.  But if a
+	 * password is required, it is specified here.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PASSWORD,
+		 g_param_spec_string (NM_SETTING_GSM_PASSWORD,
+						  "Password",
+						  "Password used to authenticate with the network, if "
+						  "required.  Note that many providers do not require "
+						  "a password or accept any password.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingGsm:apn:
+	 *
+	 * The GPRS Access Point Name specifying the APN used when establishing a
+	 * data session with the GSM-based network.  The APN often determines how
+	 * the user will be billed for their network usage and whether the user has
+	 * access to the Internet or just a provider-specific walled-garden, so it
+	 * is important to use the correct APN for the user's mobile broadband plan.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_APN,
+		 g_param_spec_string (NM_SETTING_GSM_APN,
+						  "APN",
+						  "The GPRS Access Point Name specifying the APN used "
+						  "when establishing a data session with the GSM-based "
+						  "network.  The APN often determines how the user will "
+						  "be billed for their network usage and whether the "
+						  "user has access to the Internet or just a provider-"
+						  "specific walled-garden, so it is important to use "
+						  "the correct APN for the user's mobile broadband "
+						  "plan.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingGsm:network-id:
+	 *
+	 * The Network ID (GSM LAI format, ie MCC-MNC) to force specific network
+	 * registration.  If the Network ID is specified, NetworkManager will
+	 * attempt to force the device to register only on the specified network.
+	 * This can be used to ensure that the device does not roam when direct
+	 * roaming control of the device is not otherwise possible.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NETWORK_ID,
+		 g_param_spec_string (NM_SETTING_GSM_NETWORK_ID,
+						  "Network ID",
+						  "The Network ID (GSM LAI format, ie MCC-MNC) to force "
+						  "specific network registration.  If the Network ID is "
+						  "specified, NetworkManager will attempt to force the "
+						  "device to register only on the specified network.  "
+						  "This can be used to ensure that the device does not "
+						  "roam when direct roaming control of the device is "
+						  "not otherwise possible.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingGsm:network-type:
+	 *
+	 * Network preference to force the device to only use specific network
+	 * technologies.  The permitted values are: -1: any, 0: 3G only,
+	 * 1: GPRS/EDGE only, 2: prefer 3G, and 3: prefer 2G.  Note that not all
+	 * devices allow network preference control.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NETWORK_TYPE,
+		 g_param_spec_int (NM_SETTING_GSM_NETWORK_TYPE,
+					    "Network type",
+					    "Network preference to force the device to only use "
+					    "specific network technologies.  The permitted values "
+					    "are: -1: any, 0: 3G only, 1: GPRS/EDGE only, "
+					    "2: prefer 3G, and 3: prefer 2G.  Note that not all "
+					    "devices allow network preference control.",
+					    NM_SETTING_GSM_NETWORK_TYPE_ANY,
+					    NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE,
+					    NM_SETTING_GSM_NETWORK_TYPE_ANY,
+					    G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingGsm:allowed-bands:
+	 *
+	 * Bitfield of allowed frequency bands.  Note that not all devices allow
+	 * frequency band control.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ALLOWED_BANDS,
+		 g_param_spec_uint (NM_SETTING_GSM_ALLOWED_BANDS,
+		                    "Allowed Bands",
+		                    "Bitfield of allowed frequency bands.  Note that "
+		                    "not all devices allow frequency band control.",
+		                    NM_SETTING_GSM_BAND_UNKNOWN,
+		                    NM_SETTING_GSM_BAND_UNKNOWN
+		                     || NM_SETTING_GSM_BAND_ANY
+		                     || NM_SETTING_GSM_BAND_EGSM
+		                     || NM_SETTING_GSM_BAND_DCS
+		                     || NM_SETTING_GSM_BAND_PCS
+		                     || NM_SETTING_GSM_BAND_G850
+		                     || NM_SETTING_GSM_BAND_U2100
+		                     || NM_SETTING_GSM_BAND_U1800
+		                     || NM_SETTING_GSM_BAND_U17IV
+		                     || NM_SETTING_GSM_BAND_U800
+		                     || NM_SETTING_GSM_BAND_U850
+		                     || NM_SETTING_GSM_BAND_U900
+		                     || NM_SETTING_GSM_BAND_U17IX
+		                     || NM_SETTING_GSM_BAND_U1900,
+		                    NM_SETTING_GSM_BAND_ANY,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingGsm:pin:
+	 *
+	 * If the SIM is locked with a PIN it must be unlocked before any other
+	 * operations are requested.  Specify the PIN here to allow operation of
+	 * the device.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PIN,
+		 g_param_spec_string (NM_SETTING_GSM_PIN,
+						  "PIN",
+						  "If the SIM is locked with a PIN it must be unlocked "
+						  "before any other operations are requested.  Specify "
+						  "the PIN here to allow operation of the device.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingGsm:home-only:
+	 *
+	 * When TRUE, only connections to the home network will be allowed.
+	 * Connections to roaming networks will not be made.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_HOME_ONLY,
+		 g_param_spec_boolean (NM_SETTING_GSM_HOME_ONLY,
+						  "PIN",
+						  "When TRUE, only connections to the home network will "
+						  "be allowed.  Connections to roaming networks will "
+						  "not be made.",
+						  FALSE,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/* Deprecated properties */
+	/**
+	 * NMSettingGsm:puk:
+	 *
+	 * DEPRECATED
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PUK,
+		 g_param_spec_string (NM_SETTING_GSM_PUK,
+						  "PUK (DEPRECATED and UNUSED)",
+						  "PUK (DEPRECATED and UNUSED)",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingGsm:band:
+	 *
+	 * DEPRECATED
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BAND,
+		 g_param_spec_int (NM_SETTING_GSM_BAND,
+		                    "Band (DEPRECATED and UNUSED)",
+		                    "Band (DEPRECATED and UNUSED)",
+		                    -1, 5, -1,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+}
diff --git a/libnm-util/nm-setting-gsm.h b/libnm-util/nm-setting-gsm.h
new file mode 100644
index 00000000..0ac71224
--- /dev/null
+++ b/libnm-util/nm-setting-gsm.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_GSM_H
+#define NM_SETTING_GSM_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_GSM            (nm_setting_gsm_get_type ())
+#define NM_SETTING_GSM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_GSM, NMSettingGsm))
+#define NM_SETTING_GSM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_GSM, NMSettingGsmClass))
+#define NM_IS_SETTING_GSM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_GSM))
+#define NM_IS_SETTING_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_GSM))
+#define NM_SETTING_GSM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_GSM, NMSettingGsmClass))
+
+#define NM_SETTING_GSM_SETTING_NAME "gsm"
+
+typedef enum
+{
+	NM_SETTING_GSM_ERROR_UNKNOWN = 0,
+	NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+	NM_SETTING_GSM_ERROR_MISSING_PROPERTY,
+	NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING
+} NMSettingGsmError;
+
+#define NM_TYPE_SETTING_GSM_ERROR (nm_setting_gsm_error_get_type ()) 
+GType nm_setting_gsm_error_get_type (void);
+
+#define NM_SETTING_GSM_ERROR nm_setting_gsm_error_quark ()
+GQuark nm_setting_gsm_error_quark (void);
+
+#define NM_SETTING_GSM_NUMBER        "number"
+#define NM_SETTING_GSM_USERNAME      "username"
+#define NM_SETTING_GSM_PASSWORD      "password"
+#define NM_SETTING_GSM_APN           "apn"
+#define NM_SETTING_GSM_NETWORK_ID    "network-id"
+#define NM_SETTING_GSM_NETWORK_TYPE  "network-type"
+#define NM_SETTING_GSM_ALLOWED_BANDS "allowed-bands"
+#define NM_SETTING_GSM_PIN           "pin"
+#define NM_SETTING_GSM_HOME_ONLY     "home-only"
+
+/* DEPRECATED & UNUSED */
+#define NM_SETTING_GSM_PUK           "puk"
+#define NM_SETTING_GSM_BAND          "band"
+
+/* DEPRECATED, use NM_SETTING_NETWORK_TYPE_* instead */
+#define NM_GSM_NETWORK_ANY              NM_SETTING_GSM_NETWORK_TYPE_ANY
+#define NM_GSM_NETWORK_UMTS_HSPA        NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA
+#define NM_GSM_NETWORK_GPRS_EDGE        NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE
+#define NM_GSM_NETWORK_PREFER_UMTS_HSPA NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA
+#define NM_GSM_NETWORK_PREFER_GPRS_EDGE NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE
+
+typedef enum {
+	NM_SETTING_GSM_NETWORK_TYPE_ANY = -1,
+	NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA = 0,
+	NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE = 1,
+	NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA = 2,
+	NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE = 3
+} NMSettingGsmNetworkType;
+
+typedef enum {
+	NM_SETTING_GSM_BAND_UNKNOWN      = 0x00000000,
+	NM_SETTING_GSM_BAND_ANY          = 0x00000001,
+	NM_SETTING_GSM_BAND_EGSM         = 0x00000002, /*  900 MHz */
+	NM_SETTING_GSM_BAND_DCS          = 0x00000004, /* 1800 MHz */
+	NM_SETTING_GSM_BAND_PCS          = 0x00000008, /* 1900 MHz */
+	NM_SETTING_GSM_BAND_G850         = 0x00000010, /*  850 MHz */
+	NM_SETTING_GSM_BAND_U2100        = 0x00000020, /* WCDMA 3GPP UMTS 2100 MHz     (Class I) */
+	NM_SETTING_GSM_BAND_U1800        = 0x00000040, /* WCDMA 3GPP UMTS 1800 MHz     (Class III) */
+	NM_SETTING_GSM_BAND_U17IV        = 0x00000080, /* WCDMA 3GPP AWS 1700/2100 MHz (Class IV) */
+	NM_SETTING_GSM_BAND_U800         = 0x00000100, /* WCDMA 3GPP UMTS 800 MHz      (Class VI) */
+	NM_SETTING_GSM_BAND_U850         = 0x00000200, /* WCDMA 3GPP UMTS 850 MHz      (Class V) */
+	NM_SETTING_GSM_BAND_U900         = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz      (Class VIII) */
+	NM_SETTING_GSM_BAND_U17IX        = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz     (Class IX) */
+	NM_SETTING_GSM_BAND_U1900        = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz     (Class II) */
+} NMSettingGsmNetworkBand;
+
+typedef struct {
+	NMSetting parent;
+} NMSettingGsm;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingGsmClass;
+
+GType nm_setting_gsm_get_type (void);
+
+NMSetting *nm_setting_gsm_new                (void);
+const char *nm_setting_gsm_get_number        (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_username      (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_password      (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_apn           (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_network_id    (NMSettingGsm *setting);
+int         nm_setting_gsm_get_network_type  (NMSettingGsm *setting);
+guint32     nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_pin           (NMSettingGsm *setting);
+gboolean    nm_setting_gsm_get_home_only     (NMSettingGsm *setting);
+
+/* DEPRECATED & UNUSED */
+const char *nm_setting_gsm_get_puk           (NMSettingGsm *setting);
+int         nm_setting_gsm_get_band          (NMSettingGsm *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_GSM_H */
diff --git a/libnm-util/nm-setting-ip4-config.c b/libnm-util/nm-setting-ip4-config.c
new file mode 100644
index 00000000..186e41e2
--- /dev/null
+++ b/libnm-util/nm-setting-ip4-config.c
@@ -0,0 +1,1287 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include "nm-setting-ip4-config.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+
+GQuark
+nm_setting_ip4_config_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-ip4-config-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_ip4_config_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_IP4_CONFIG_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The specified property was not allowed in combination with the current 'method' */
+			ENUM_ENTRY (NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, "NotAllowedForMethod"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingIP4ConfigError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING)
+
+#define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate))
+
+typedef struct {
+	char *method;
+	GArray *dns;        /* array of guint32; elements in network byte order */
+	GSList *dns_search; /* list of strings */
+	GSList *addresses;  /* array of NMIP4Address */
+	GSList *routes;     /* array of NMIP4Route */
+	gboolean ignore_auto_routes;
+	gboolean ignore_auto_dns;
+	char *dhcp_client_id;
+	gboolean dhcp_send_hostname;
+	char *dhcp_hostname;
+	gboolean never_default;
+	gboolean may_fail;
+} NMSettingIP4ConfigPrivate;
+
+enum {
+	PROP_0,
+	PROP_METHOD,
+	PROP_DNS,
+	PROP_DNS_SEARCH,
+	PROP_ADDRESSES,
+	PROP_ROUTES,
+	PROP_IGNORE_AUTO_ROUTES,
+	PROP_IGNORE_AUTO_DNS,
+	PROP_DHCP_CLIENT_ID,
+	PROP_DHCP_SEND_HOSTNAME,
+	PROP_DHCP_HOSTNAME,
+	PROP_NEVER_DEFAULT,
+	PROP_MAY_FAIL,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_ip4_config_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP4_CONFIG, NULL);
+}
+
+const char *
+nm_setting_ip4_config_get_method (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->method;
+}
+
+guint32
+nm_setting_ip4_config_get_num_dns (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns->len;
+}
+
+guint32
+nm_setting_ip4_config_get_dns (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= priv->dns->len, 0);
+
+	return g_array_index (priv->dns, guint32, i);
+}
+
+gboolean
+nm_setting_ip4_config_add_dns (NMSettingIP4Config *setting, guint32 dns)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	int i;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	for (i = 0; i < priv->dns->len; i++) {
+		if (dns == g_array_index (priv->dns, guint32, i))
+			return FALSE;
+	}
+
+	g_array_append_val (priv->dns, dns);
+	return TRUE;
+}
+
+void
+nm_setting_ip4_config_remove_dns (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	g_return_if_fail (i <= priv->dns->len);
+
+	g_array_remove_index (priv->dns, i);
+}
+
+void
+nm_setting_ip4_config_clear_dns (NMSettingIP4Config *setting)
+{
+	NMSettingIP4ConfigPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	g_array_remove_range (priv->dns, 0, priv->dns->len);
+}
+
+guint32
+nm_setting_ip4_config_get_num_dns_searches (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search);
+}
+
+const char *
+nm_setting_ip4_config_get_dns_search (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->dns_search), NULL);
+
+	return (const char *) g_slist_nth_data (priv->dns_search, i);
+}
+
+gboolean
+nm_setting_ip4_config_add_dns_search (NMSettingIP4Config *setting,
+                                      const char *dns_search)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+	g_return_val_if_fail (dns_search != NULL, FALSE);
+	g_return_val_if_fail (dns_search[0] != '\0', FALSE);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (dns_search, (char *) iter->data))
+			return FALSE;
+	}
+
+	priv->dns_search = g_slist_append (priv->dns_search, g_strdup (dns_search));
+	return TRUE;
+}
+
+void
+nm_setting_ip4_config_remove_dns_search (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->dns_search, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->dns_search = g_slist_delete_link (priv->dns_search, elt);
+}
+
+void
+nm_setting_ip4_config_clear_dns_searches (NMSettingIP4Config *setting)
+{
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	nm_utils_slist_free (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search, g_free);
+	NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search = NULL;
+}
+
+guint32
+nm_setting_ip4_config_get_num_addresses (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->addresses);
+}
+
+NMIP4Address *
+nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->addresses), NULL);
+
+	return (NMIP4Address *) g_slist_nth_data (priv->addresses, i);
+}
+
+gboolean
+nm_setting_ip4_config_add_address (NMSettingIP4Config *setting,
+                                   NMIP4Address *address)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	NMIP4Address *copy;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+	g_return_val_if_fail (address != NULL, FALSE);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
+		if (nm_ip4_address_compare ((NMIP4Address *) iter->data, address))
+			return FALSE;
+	}
+
+	copy = nm_ip4_address_dup (address);
+	g_return_val_if_fail (copy != NULL, FALSE);
+
+	priv->addresses = g_slist_append (priv->addresses, copy);
+	return TRUE;
+}
+
+void
+nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->addresses, i);
+	g_return_if_fail (elt != NULL);
+
+	nm_ip4_address_unref ((NMIP4Address *) elt->data);
+	priv->addresses = g_slist_delete_link (priv->addresses, elt);
+}
+
+void
+nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting)
+{
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+	priv->addresses = NULL;
+}
+
+guint32
+nm_setting_ip4_config_get_num_routes (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->routes);
+}
+
+NMIP4Route *
+nm_setting_ip4_config_get_route (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->routes), NULL);
+
+	return (NMIP4Route *) g_slist_nth_data (priv->routes, i);
+}
+
+gboolean
+nm_setting_ip4_config_add_route (NMSettingIP4Config *setting,
+                                 NMIP4Route *route)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	NMIP4Route *copy;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+	g_return_val_if_fail (route != NULL, FALSE);
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->routes; iter; iter = g_slist_next (iter)) {
+		if (nm_ip4_route_compare ((NMIP4Route *) iter->data, route))
+			return FALSE;
+	}
+
+	copy = nm_ip4_route_dup (route);
+	g_return_val_if_fail (copy != NULL, FALSE);
+
+	priv->routes = g_slist_append (priv->routes, copy);
+	return TRUE;
+}
+
+void
+nm_setting_ip4_config_remove_route (NMSettingIP4Config *setting, guint32 i)
+{
+	NMSettingIP4ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->routes, i);
+	g_return_if_fail (elt != NULL);
+
+	nm_ip4_route_unref ((NMIP4Route *) elt->data);
+	priv->routes = g_slist_delete_link (priv->routes, elt);
+}
+
+void
+nm_setting_ip4_config_clear_routes (NMSettingIP4Config *setting)
+{
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+	nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+	priv->routes = NULL;
+}
+
+gboolean
+nm_setting_ip4_config_get_ignore_auto_routes (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->ignore_auto_routes;
+}
+
+gboolean
+nm_setting_ip4_config_get_ignore_auto_dns (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->ignore_auto_dns;
+}
+
+const char *
+nm_setting_ip4_config_get_dhcp_client_id (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_client_id;
+}
+
+gboolean
+nm_setting_ip4_config_get_dhcp_send_hostname (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_send_hostname;
+}
+
+const char *
+nm_setting_ip4_config_get_dhcp_hostname (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_hostname;
+}
+
+gboolean
+nm_setting_ip4_config_get_never_default (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->never_default;
+}
+
+gboolean
+nm_setting_ip4_config_get_may_fail (NMSettingIP4Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->may_fail;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+	GSList *iter;
+	int i;
+
+	if (!priv->method) {
+		g_set_error (error,
+		             NM_SETTING_IP4_CONFIG_ERROR,
+		             NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_IP4_CONFIG_METHOD);
+		return FALSE;
+	}
+
+	if (!strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+		if (!priv->addresses) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_IP4_CONFIG_ADDRESSES);
+			return FALSE;
+		}
+	} else if (   !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)
+	           || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)
+	           || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
+		if (priv->dns && priv->dns->len) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+			             NM_SETTING_IP4_CONFIG_DNS);
+			return FALSE;
+		}
+
+		if (g_slist_length (priv->dns_search)) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+			             NM_SETTING_IP4_CONFIG_DNS_SEARCH);
+			return FALSE;
+		}
+
+		if (g_slist_length (priv->addresses)) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+			             NM_SETTING_IP4_CONFIG_ADDRESSES);
+			return FALSE;
+		}
+	} else if (!strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+		/* nothing to do */
+	} else {
+		g_set_error (error,
+		             NM_SETTING_IP4_CONFIG_ERROR,
+		             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_IP4_CONFIG_METHOD);
+		return FALSE;
+	}
+
+	if (priv->dhcp_client_id && !strlen (priv->dhcp_client_id)) {
+		g_set_error (error,
+		             NM_SETTING_IP4_CONFIG_ERROR,
+		             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID);
+		return FALSE;
+	}
+
+	if (priv->dhcp_hostname && !strlen (priv->dhcp_hostname)) {
+		g_set_error (error,
+		             NM_SETTING_IP4_CONFIG_ERROR,
+		             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME);
+		return FALSE;
+	}
+
+	/* Validate addresses */
+	for (iter = priv->addresses, i = 0; iter; iter = g_slist_next (iter), i++) {
+		NMIP4Address *addr = (NMIP4Address *) iter->data;
+		guint32 prefix = nm_ip4_address_get_prefix (addr);
+
+		if (!nm_ip4_address_get_address (addr)) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_IP4_CONFIG_ADDRESSES);
+			return FALSE;
+		}
+
+		if (!prefix || prefix > 32) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_IP4_CONFIG_ADDRESSES);
+			return FALSE;
+		}
+	}
+
+	/* Validate routes */
+	for (iter = priv->routes, i = 0; iter; iter = g_slist_next (iter), i++) {
+		NMIP4Route *route = (NMIP4Route *) iter->data;
+		guint32 prefix = nm_ip4_route_get_prefix (route);
+
+		if (!nm_ip4_route_get_dest (route)) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_IP4_CONFIG_ROUTES);
+			return FALSE;
+		}
+
+		if (!prefix || prefix > 32) {
+			g_set_error (error,
+			             NM_SETTING_IP4_CONFIG_ERROR,
+			             NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_IP4_CONFIG_ROUTES);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+
+static void
+nm_setting_ip4_config_init (NMSettingIP4Config *setting)
+{
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL);
+
+	priv->dns = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingIP4Config *self = NM_SETTING_IP4_CONFIG (object);
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (self);
+
+	g_free (priv->method);
+
+	g_array_free (priv->dns, TRUE);
+
+	nm_utils_slist_free (priv->dns_search, g_free);
+	nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+	nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+
+	G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object);
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_METHOD:
+		g_free (priv->method);
+		priv->method = g_value_dup_string (value);
+		break;
+	case PROP_DNS:
+		g_array_free (priv->dns, TRUE);
+		priv->dns = g_value_dup_boxed (value);
+		if (!priv->dns)
+			priv->dns = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);			
+		break;
+	case PROP_DNS_SEARCH:
+		nm_utils_slist_free (priv->dns_search, g_free);
+		priv->dns_search = g_value_dup_boxed (value);
+		break;
+	case PROP_ADDRESSES:
+		nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+		priv->addresses = nm_utils_ip4_addresses_from_gvalue (value);
+		break;
+	case PROP_ROUTES:
+		nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+		priv->routes = nm_utils_ip4_routes_from_gvalue (value);
+		break;
+	case PROP_IGNORE_AUTO_ROUTES:
+		priv->ignore_auto_routes = g_value_get_boolean (value);
+		break;
+	case PROP_IGNORE_AUTO_DNS:
+		priv->ignore_auto_dns = g_value_get_boolean (value);
+		break;
+	case PROP_DHCP_CLIENT_ID:
+		g_free (priv->dhcp_client_id);
+		priv->dhcp_client_id = g_value_dup_string (value);
+		break;
+	case PROP_DHCP_SEND_HOSTNAME:
+		priv->dhcp_send_hostname = g_value_get_boolean (value);
+		break;
+	case PROP_DHCP_HOSTNAME:
+		g_free (priv->dhcp_hostname);
+		priv->dhcp_hostname = g_value_dup_string (value);
+		/* FIXME: Is this a good idea? */
+		if (priv->dhcp_hostname)
+			priv->dhcp_send_hostname = TRUE;
+		break;
+	case PROP_NEVER_DEFAULT:
+		priv->never_default = g_value_get_boolean (value);
+		break;
+	case PROP_MAY_FAIL:
+		priv->may_fail = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object);
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_METHOD:
+		g_value_set_string (value, nm_setting_ip4_config_get_method (setting));
+		break;
+	case PROP_DNS:
+		g_value_set_boxed (value, priv->dns);
+		break;
+	case PROP_DNS_SEARCH:
+		g_value_set_boxed (value, priv->dns_search);
+		break;
+	case PROP_ADDRESSES:
+		nm_utils_ip4_addresses_to_gvalue (priv->addresses, value);
+		break;
+	case PROP_ROUTES:
+		nm_utils_ip4_routes_to_gvalue (priv->routes, value);
+		break;
+	case PROP_IGNORE_AUTO_ROUTES:
+		g_value_set_boolean (value, nm_setting_ip4_config_get_ignore_auto_routes (setting));
+		break;
+	case PROP_IGNORE_AUTO_DNS:
+		g_value_set_boolean (value, nm_setting_ip4_config_get_ignore_auto_dns (setting));
+		break;
+	case PROP_DHCP_CLIENT_ID:
+		g_value_set_string (value, nm_setting_ip4_config_get_dhcp_client_id (setting));
+		break;
+	case PROP_DHCP_SEND_HOSTNAME:
+		g_value_set_boolean (value, nm_setting_ip4_config_get_dhcp_send_hostname (setting));
+		break;
+	case PROP_DHCP_HOSTNAME:
+		g_value_set_string (value, nm_setting_ip4_config_get_dhcp_hostname (setting));
+		break;
+	case PROP_NEVER_DEFAULT:
+		g_value_set_boolean (value, priv->never_default);
+		break;
+	case PROP_MAY_FAIL:
+		g_value_set_boolean (value, priv->may_fail);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingIP4ConfigPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+	/**
+	 * NMSettingIP4Config:method:
+	 *
+	 * IPv4 configuration method.  If 'auto' is specified then the appropriate
+	 * automatic method (DHCP, PPP, etc) is used for the interface and most
+	 * other properties can be left unset.  If 'link-local' is specified, then a
+	 * link-local address in the 169.254/16 range will be assigned to the
+	 * interface.  If 'manual' is specified, static IP addressing is used and at
+	 * least one IP address must be given in the 'addresses' property.  If
+	 * 'shared' is specified (indicating that this connection will provide
+	 * network access to other computers) then the interface is assigned an
+	 * address in the 10.42.x.1/24 range and a DHCP and forwarding DNS server
+	 * are started, and the interface is NAT-ed to the current default network
+	 * connection.  'disabled' means IPv4 will not be used on this connection.
+	 * This property must be set.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_METHOD,
+		 g_param_spec_string (NM_SETTING_IP4_CONFIG_METHOD,
+						      "Method",
+						      "IPv4 configuration method.  If 'auto' is specified "
+						      "then the appropriate automatic method (DHCP, PPP, "
+						      "etc) is used for the interface and most other "
+						      "properties can be left unset.  If 'link-local' "
+						      "is specified, then a link-local address in the "
+						      "169.254/16 range will be assigned to the "
+						      "interface.  If 'manual' is specified, static IP "
+						      "addressing is used and at least one IP address "
+						      "must be given in the 'addresses' property.  If "
+						      "'shared' is specified (indicating that this "
+						      "connection will provide network access to other "
+						      "computers) then the interface is assigned an "
+						      "address in the 10.42.x.1/24 range and a DHCP and "
+						      "forwarding DNS server are started, and the "
+						      "interface is NAT-ed to the current default network "
+						      "connection.  'disabled' means IPv4 will not be "
+						      "used on this connection.  This property must be set.",
+						      NULL,
+						      G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:dns:
+	 *
+	 * List of DNS servers (network byte order).  For the 'auto' method, these
+	 * DNS servers are appended to those (if any) returned by automatic
+	 * configuration.  DNS servers cannot be used with the 'shared', 'link-local',
+	 * or 'disabled' methods as there is no usptream network.  In all other
+	 * methods, these DNS servers are used as the only DNS servers for this
+	 * connection.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DNS,
+		 _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_DNS,
+							   "DNS",
+							   "List of DNS servers (network byte order). For "
+							   "the 'auto' method, these DNS servers are "
+							   "appended to those (if any) returned by automatic "
+							   "configuration.  DNS servers cannot be used with "
+							   "the 'shared', 'link-local', or 'disabled' "
+							   "methods as there is no usptream network.  In all "
+							   "other methods, these DNS servers are used as the "
+							   "only DNS servers for this connection.",
+							   DBUS_TYPE_G_UINT_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:dns-search:
+	 *
+	 * List of DNS search domains.  For the 'auto' method, these search domains
+	 * are appended to those returned by automatic configuration. Search domains
+	 * cannot be used with the 'shared', 'link-local', or 'disabled' methods as
+	 * there is no upstream network.  In all other methods, these search domains
+	 * are used as the only search domains for this connection.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DNS_SEARCH,
+		 _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_DNS_SEARCH,
+							   "DNS search",
+							   "List of DNS search domains.  For the 'auto' "
+							   "method, these search domains are appended to "
+							   "those returned by automatic configuration. "
+							   "Search domains cannot be used with the 'shared', "
+							   "'link-local', or 'disabled' methods as there is "
+							   "no upstream network.  In all other methods, these "
+							   "search domains are used as the only search domains "
+							   "for this connection.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:addresses:
+	 *
+	 * Array of IPv4 address structures.  Each IPv4 address structure is
+	 * composed of 3 32-bit values; the first being the IPv4 address (network
+	 * byte order), the second the prefix (1 - 32), and last the IPv4 gateway
+	 * (network byte order). The gateway may be left as 0 if no gateway exists
+	 * for that subnet.  For the 'auto' method, given IP addresses are appended
+	 * to those returned by automatic configuration.  Addresses cannot be used
+	 * with the 'shared', 'link-local', or 'disabled' methods as addressing is
+	 * either automatic or disabled with these methods.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ADDRESSES,
+		 _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_ADDRESSES,
+							   "Addresses",
+							   "Array of IPv4 address structures.  Each IPv4 "
+							   "address structure is composed of 3 32-bit values; "
+							   "the first being the IPv4 address (network byte "
+							   "order), the second the prefix (1 - 32), and "
+							   "last the IPv4 gateway (network byte order). The "
+							   "gateway may be left as 0 if no gateway exists "
+							   "for that subnet.  For the 'auto' method, given "
+							   "IP addresses are appended to those returned by "
+							   "automatic configuration.  Addresses cannot be "
+							   "used with the 'shared', 'link-local', or "
+							   "'disabled' methods as addressing is either "
+							   "automatic or disabled with these methods.",
+							   DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:routes:
+	 *
+	 * Array of IPv4 route structures.  Each IPv4 route structure is composed
+	 * of 4 32-bit values; the first being the destination IPv4 network or
+	 * address (network byte order), the second the destination network or
+	 * address prefix (1 - 32), the third being the next-hop (network byte
+	 * order) if any, and the fourth being the route metric. For the 'auto'
+	 * method, given IP routes are appended to those returned by automatic
+	 * configuration.  Routes cannot be used with the 'shared', 'link-local',
+	 * or 'disabled' methods because there is no upstream network.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ROUTES,
+		 _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_ROUTES,
+							   "Routes",
+							   "Array of IPv4 route structures.  Each IPv4 route "
+							   "structure is composed of 4 32-bit values; the "
+							   "first being the destination IPv4 network or "
+							   "address (network byte order), the second the "
+							   "destination network or address prefix (1 - 32), "
+							   "the third being the next-hop (network byte order) "
+							   "if any, and the fourth being the route metric. "
+							   "For the 'auto' method, given IP routes are "
+							   "appended to those returned by automatic "
+							   "configuration.  Routes cannot be used with the "
+							   "'shared', 'link-local', or 'disabled', methods "
+							   "as there is no upstream network.",
+							   DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:ignore-auto-routes:
+	 *
+	 * When the method is set to 'auto' and this property to TRUE, automatically
+	 * configured routes are ignored and only routes specified in
+	 * #NMSettingIP4Config:routes, if any, are used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_IGNORE_AUTO_ROUTES,
+		 g_param_spec_boolean (NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES,
+						   "Ignore automatic routes",
+						   "When the method is set to 'auto' and this property "
+						   "to TRUE, automatically configured routes are "
+						   "ignored and only routes specified in the 'routes' "
+						   "property, if any, are used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:ignore-auto-dns:
+	 *
+	 * When the method is set to 'auto' and this property to TRUE, automatically
+	 * configured nameservers and search domains are ignored and only nameservers
+	 * and search domains specified in #NMSettingIP4Config:dns and
+	 * #NMSettingIP4Config:dns-search, if any, are used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_IGNORE_AUTO_DNS,
+		 g_param_spec_boolean (NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS,
+						   "Ignore automatic DNS",
+						   "When the method is set to 'auto' and this property "
+						   "to TRUE, automatically configured nameservers and "
+						   "search domains are ignored and only nameservers and "
+						   "search domains specified in the 'dns' and 'dns-search' "
+						   "properties, if any, are used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:dhcp-client-id:
+	 *
+	 * A string sent to the DHCP server to identify the local machine which the
+	 * DHCP server may use to cusomize the DHCP lease and options.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DHCP_CLIENT_ID,
+		 g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,
+						   "DHCP Client ID",
+						   "A string sent to the DHCP server to identify the "
+						   "local machine which the DHCP server may use to "
+						   "cusomize the DHCP lease and options.",
+						   NULL,
+						   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:dhcp-send-hostname:
+	 *
+	 * If TRUE, a hostname is sent to the DHCP server when acquiring a lease.
+	 * Some DHCP servers use this hostname to update DNS databases, essentially
+	 * providing a static hostname for the computer.  If
+	 * #NMSettingIP4Config:dhcp-hostname is empty and this property is TRUE,
+	 * the current persistent hostname of the computer is sent.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DHCP_SEND_HOSTNAME,
+		 g_param_spec_boolean (NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME,
+						   "Send DHCP hostname",
+						   "If TRUE, a hostname is sent to the DHCP server when "
+						   "acquiring a lease.  Some DHCP servers use this "
+						   "hostname to update DNS databases, essentially "
+						   "providing a static hostname for the computer.  If "
+						   "the 'dhcp-hostname' property is empty and this "
+						   "property is TRUE, the current persistent hostname "
+						   "of the computer is sent.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:dhcp-hostname:
+	 *
+	 * If the #NMSettingIP4Config:dhcp-send-hostname property is TRUE, then the
+	 * specified name will be sent to the DHCP server when acquiring a lease.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DHCP_HOSTNAME,
+		 g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME,
+						   "DHCP Hostname",
+						   "If the 'dhcp-send-hostname' property is TRUE, then "
+						   "the specified name will be sent to the DHCP server "
+						   "when acquiring a lease.",
+						   NULL,
+						   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:never-default:
+	 *
+	 * If TRUE, this connection will never be the default IPv4 connection,
+	 * meaning it will never be assigned the default route by NetworkManager.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NEVER_DEFAULT,
+		 g_param_spec_boolean (NM_SETTING_IP4_CONFIG_NEVER_DEFAULT,
+						   "Never default",
+						   "If TRUE, this connection will never be the default "
+						   "IPv4 connection, meaning it will never be assigned "
+						   "the default route by NetworkManager.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP4Config:may-fail:
+	 *
+	 * If TRUE, allow overall network configuration to proceed even if IPv4
+	 * configuration times out.  Note that at least one IP configuration
+	 * must succeed or overall network configuration will still fail.  For
+	 * example, in IPv6-only networks, setting this property to TRUE allows
+	 * the overall network configuration to succeed if IPv4 configuration fails
+	 * but IPv6 configuration completes successfully.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MAY_FAIL,
+		 g_param_spec_boolean (NM_SETTING_IP4_CONFIG_MAY_FAIL,
+						   "May Fail",
+						   "If TRUE, allow overall network configuration to "
+						   "proceed even if IPv4 configuration times out. "
+						   "Note that at least one IP configuration must "
+						   "succeed or overall network configuration will still "
+						   "fail.  For example, in IPv6-only networks, setting "
+						   "this property to TRUE allows the overall network "
+						   "configuration to succeed if IPv4 configuration "
+						   "fails but IPv6 configuration completes successfully.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+}
+
+
+struct NMIP4Address {
+	guint32 refcount;
+	guint32 address;   /* network byte order */
+	guint32 prefix;
+	guint32 gateway;   /* network byte order */
+};
+
+NMIP4Address *
+nm_ip4_address_new (void)
+{
+	NMIP4Address *address;
+
+	address = g_malloc0 (sizeof (NMIP4Address));
+	address->refcount = 1;
+	return address;
+}
+
+NMIP4Address *
+nm_ip4_address_dup (NMIP4Address *source)
+{
+	NMIP4Address *address;
+
+	g_return_val_if_fail (source != NULL, NULL);
+	g_return_val_if_fail (source->refcount > 0, NULL);
+
+	address = nm_ip4_address_new ();
+	address->address = source->address;
+	address->prefix = source->prefix;
+	address->gateway = source->gateway;
+
+	return address;
+}
+
+void
+nm_ip4_address_ref (NMIP4Address *address)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->refcount++;
+}
+
+void
+nm_ip4_address_unref (NMIP4Address *address)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->refcount--;
+	if (address->refcount == 0) {
+		memset (address, 0, sizeof (NMIP4Address));
+		g_free (address);
+	}
+}
+
+gboolean
+nm_ip4_address_compare (NMIP4Address *address, NMIP4Address *other)
+{
+	g_return_val_if_fail (address != NULL, FALSE);
+	g_return_val_if_fail (address->refcount > 0, FALSE);
+
+	g_return_val_if_fail (other != NULL, FALSE);
+	g_return_val_if_fail (other->refcount > 0, FALSE);
+
+	if (   address->address != other->address
+	    || address->prefix != other->prefix
+	    || address->gateway != other->gateway)
+		return FALSE;
+	return TRUE;
+}
+
+guint32
+nm_ip4_address_get_address (NMIP4Address *address)
+{
+	g_return_val_if_fail (address != NULL, 0);
+	g_return_val_if_fail (address->refcount > 0, 0);
+
+	return address->address;
+}
+
+void
+nm_ip4_address_set_address (NMIP4Address *address, guint32 addr)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->address = addr;
+}
+
+guint32
+nm_ip4_address_get_prefix (NMIP4Address *address)
+{
+	g_return_val_if_fail (address != NULL, 0);
+	g_return_val_if_fail (address->refcount > 0, 0);
+
+	return address->prefix;
+}
+
+void
+nm_ip4_address_set_prefix (NMIP4Address *address, guint32 prefix)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->prefix = prefix;
+}
+
+guint32
+nm_ip4_address_get_gateway (NMIP4Address *address)
+{
+	g_return_val_if_fail (address != NULL, 0);
+	g_return_val_if_fail (address->refcount > 0, 0);
+
+	return address->gateway;
+}
+
+void
+nm_ip4_address_set_gateway (NMIP4Address *address, guint32 gateway)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->gateway = gateway;
+}
+
+
+struct NMIP4Route {
+	guint32 refcount;
+
+	guint32 dest;   /* network byte order */
+	guint32 prefix;
+	guint32 next_hop;   /* network byte order */
+	guint32 metric;    /* lower metric == more preferred */
+};
+
+NMIP4Route *
+nm_ip4_route_new (void)
+{
+	NMIP4Route *route;
+
+	route = g_malloc0 (sizeof (NMIP4Route));
+	route->refcount = 1;
+	return route;
+}
+
+NMIP4Route *
+nm_ip4_route_dup (NMIP4Route *source)
+{
+	NMIP4Route *route;
+
+	g_return_val_if_fail (source != NULL, NULL);
+	g_return_val_if_fail (source->refcount > 0, NULL);
+
+	route = nm_ip4_route_new ();
+	route->dest = source->dest;
+	route->prefix = source->prefix;
+	route->next_hop = source->next_hop;
+	route->metric = source->metric;
+
+	return route;
+}
+
+void
+nm_ip4_route_ref (NMIP4Route *route)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->refcount++;
+}
+
+void
+nm_ip4_route_unref (NMIP4Route *route)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->refcount--;
+	if (route->refcount == 0) {
+		memset (route, 0, sizeof (NMIP4Route));
+		g_free (route);
+	}
+}
+
+gboolean
+nm_ip4_route_compare (NMIP4Route *route, NMIP4Route *other)
+{
+	g_return_val_if_fail (route != NULL, FALSE);
+	g_return_val_if_fail (route->refcount > 0, FALSE);
+
+	g_return_val_if_fail (other != NULL, FALSE);
+	g_return_val_if_fail (other->refcount > 0, FALSE);
+
+	if (   route->dest != other->dest
+	    || route->prefix != other->prefix
+	    || route->next_hop != other->next_hop
+	    || route->metric != other->metric)
+		return FALSE;
+	return TRUE;
+}
+
+guint32
+nm_ip4_route_get_dest (NMIP4Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return route->dest;
+}
+
+void
+nm_ip4_route_set_dest (NMIP4Route *route, guint32 dest)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->dest = dest;
+}
+
+guint32
+nm_ip4_route_get_prefix (NMIP4Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return route->prefix;
+}
+
+void
+nm_ip4_route_set_prefix (NMIP4Route *route, guint32 prefix)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->prefix = prefix;
+}
+
+guint32
+nm_ip4_route_get_next_hop (NMIP4Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return route->next_hop;
+}
+
+void
+nm_ip4_route_set_next_hop (NMIP4Route *route, guint32 next_hop)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->next_hop = next_hop;
+}
+
+guint32
+nm_ip4_route_get_metric (NMIP4Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return route->metric;
+}
+
+void
+nm_ip4_route_set_metric (NMIP4Route *route, guint32 metric)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->metric = metric;
+}
+
diff --git a/libnm-util/nm-setting-ip4-config.h b/libnm-util/nm-setting-ip4-config.h
new file mode 100644
index 00000000..80ddd475
--- /dev/null
+++ b/libnm-util/nm-setting-ip4-config.h
@@ -0,0 +1,177 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_IP4_CONFIG_H
+#define NM_SETTING_IP4_CONFIG_H
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_IP4_CONFIG            (nm_setting_ip4_config_get_type ())
+#define NM_SETTING_IP4_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4Config))
+#define NM_SETTING_IP4_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IP4CONFIG, NMSettingIP4ConfigClass))
+#define NM_IS_SETTING_IP4_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_IP4_CONFIG))
+#define NM_IS_SETTING_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_IP4_CONFIG))
+#define NM_SETTING_IP4_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigClass))
+
+#define NM_SETTING_IP4_CONFIG_SETTING_NAME "ipv4"
+
+typedef enum
+{
+	NM_SETTING_IP4_CONFIG_ERROR_UNKNOWN = 0,
+	NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+	NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY,
+	NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD
+} NMSettingIP4ConfigError;
+
+#define NM_TYPE_SETTING_IP4_CONFIG_ERROR (nm_setting_ip4_config_error_get_type ()) 
+GType nm_setting_ip4_config_error_get_type (void);
+
+#define NM_SETTING_IP4_CONFIG_ERROR nm_setting_ip4_config_error_quark ()
+GQuark nm_setting_ip4_config_error_quark (void);
+
+#define NM_SETTING_IP4_CONFIG_METHOD             "method"
+#define NM_SETTING_IP4_CONFIG_DNS                "dns"
+#define NM_SETTING_IP4_CONFIG_DNS_SEARCH         "dns-search"
+#define NM_SETTING_IP4_CONFIG_ADDRESSES          "addresses"
+#define NM_SETTING_IP4_CONFIG_ROUTES             "routes"
+#define NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes"
+#define NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS    "ignore-auto-dns"
+#define NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID     "dhcp-client-id"
+#define NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME "dhcp-send-hostname"
+#define NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME      "dhcp-hostname"
+#define NM_SETTING_IP4_CONFIG_NEVER_DEFAULT      "never-default"
+#define NM_SETTING_IP4_CONFIG_MAY_FAIL           "may-fail"
+
+#define NM_SETTING_IP4_CONFIG_METHOD_AUTO       "auto"
+#define NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL "link-local"
+#define NM_SETTING_IP4_CONFIG_METHOD_MANUAL     "manual"
+#define NM_SETTING_IP4_CONFIG_METHOD_SHARED     "shared"
+#define NM_SETTING_IP4_CONFIG_METHOD_DISABLED   "disabled"
+
+typedef struct NMIP4Address NMIP4Address;
+
+NMIP4Address * nm_ip4_address_new         (void);
+NMIP4Address * nm_ip4_address_dup         (NMIP4Address *source);
+void           nm_ip4_address_ref         (NMIP4Address *address);
+void           nm_ip4_address_unref       (NMIP4Address *address);
+/* Return TRUE if addresses are identical */
+gboolean       nm_ip4_address_compare     (NMIP4Address *address, NMIP4Address *other);
+
+guint32        nm_ip4_address_get_address (NMIP4Address *address);
+void           nm_ip4_address_set_address (NMIP4Address *address,
+                                           guint32 addr);  /* network byte order */
+
+guint32        nm_ip4_address_get_prefix  (NMIP4Address *address);
+void           nm_ip4_address_set_prefix  (NMIP4Address *address,
+                                           guint32 prefix);
+
+guint32        nm_ip4_address_get_gateway (NMIP4Address *address);
+void           nm_ip4_address_set_gateway (NMIP4Address *address,
+                                           guint32 gateway);  /* network byte order */
+
+typedef struct NMIP4Route NMIP4Route;
+
+NMIP4Route * nm_ip4_route_new          (void);
+NMIP4Route * nm_ip4_route_dup          (NMIP4Route *route);
+void         nm_ip4_route_ref          (NMIP4Route *route);
+void         nm_ip4_route_unref        (NMIP4Route *route);
+/* Return TRUE if routes are identical */
+gboolean     nm_ip4_route_compare      (NMIP4Route *route, NMIP4Route *other);
+
+guint32      nm_ip4_route_get_dest     (NMIP4Route *route);
+void         nm_ip4_route_set_dest     (NMIP4Route *route,
+                                        guint32 dest);  /* network byte order */
+
+guint32      nm_ip4_route_get_prefix   (NMIP4Route *route);
+void         nm_ip4_route_set_prefix   (NMIP4Route *route,
+                                        guint32 prefix);
+
+guint32      nm_ip4_route_get_next_hop (NMIP4Route *route);
+void         nm_ip4_route_set_next_hop (NMIP4Route *route,
+                                        guint32 next_hop);  /* network byte order */
+
+guint32      nm_ip4_route_get_metric   (NMIP4Route *route);
+void         nm_ip4_route_set_metric   (NMIP4Route *route,
+                                        guint32 metric);
+
+
+typedef struct {
+	NMSetting parent;
+} NMSettingIP4Config;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingIP4ConfigClass;
+
+GType nm_setting_ip4_config_get_type (void);
+
+NMSetting *   nm_setting_ip4_config_new                    (void);
+const char *  nm_setting_ip4_config_get_method             (NMSettingIP4Config *setting);
+
+guint32       nm_setting_ip4_config_get_num_dns            (NMSettingIP4Config *setting);
+guint32       nm_setting_ip4_config_get_dns                (NMSettingIP4Config *setting, guint32 i);
+gboolean      nm_setting_ip4_config_add_dns                (NMSettingIP4Config *setting, guint32 dns);
+void          nm_setting_ip4_config_remove_dns             (NMSettingIP4Config *setting, guint32 i);
+void          nm_setting_ip4_config_clear_dns              (NMSettingIP4Config *setting);
+
+guint32       nm_setting_ip4_config_get_num_dns_searches   (NMSettingIP4Config *setting);
+const char *  nm_setting_ip4_config_get_dns_search         (NMSettingIP4Config *setting, guint32 i);
+gboolean      nm_setting_ip4_config_add_dns_search         (NMSettingIP4Config *setting, const char *dns_search);
+void          nm_setting_ip4_config_remove_dns_search      (NMSettingIP4Config *setting, guint32 i);
+void          nm_setting_ip4_config_clear_dns_searches     (NMSettingIP4Config *setting);
+
+guint32       nm_setting_ip4_config_get_num_addresses      (NMSettingIP4Config *setting);
+NMIP4Address *nm_setting_ip4_config_get_address            (NMSettingIP4Config *setting, guint32 i);
+gboolean      nm_setting_ip4_config_add_address            (NMSettingIP4Config *setting, NMIP4Address *address);
+void          nm_setting_ip4_config_remove_address         (NMSettingIP4Config *setting, guint32 i);
+void          nm_setting_ip4_config_clear_addresses        (NMSettingIP4Config *setting);
+
+guint32       nm_setting_ip4_config_get_num_routes         (NMSettingIP4Config *setting);
+NMIP4Route *  nm_setting_ip4_config_get_route              (NMSettingIP4Config *setting, guint32 i);
+gboolean      nm_setting_ip4_config_add_route              (NMSettingIP4Config *setting, NMIP4Route *route);
+void          nm_setting_ip4_config_remove_route           (NMSettingIP4Config *setting, guint32 i);
+void          nm_setting_ip4_config_clear_routes           (NMSettingIP4Config *setting);
+
+gboolean      nm_setting_ip4_config_get_ignore_auto_routes (NMSettingIP4Config *setting);
+gboolean      nm_setting_ip4_config_get_ignore_auto_dns    (NMSettingIP4Config *setting);
+const char *  nm_setting_ip4_config_get_dhcp_client_id     (NMSettingIP4Config *setting);
+gboolean      nm_setting_ip4_config_get_dhcp_send_hostname (NMSettingIP4Config *setting);
+const char *  nm_setting_ip4_config_get_dhcp_hostname      (NMSettingIP4Config *setting);
+
+gboolean      nm_setting_ip4_config_get_never_default      (NMSettingIP4Config *setting);
+
+gboolean      nm_setting_ip4_config_get_may_fail           (NMSettingIP4Config *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_IP4_CONFIG_H */
diff --git a/libnm-util/nm-setting-ip6-config.c b/libnm-util/nm-setting-ip6-config.c
new file mode 100644
index 00000000..32c3a4a2
--- /dev/null
+++ b/libnm-util/nm-setting-ip6-config.c
@@ -0,0 +1,1120 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * David Cantrell <dcantrel@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include "nm-setting-ip6-config.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+
+GQuark
+nm_setting_ip6_config_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-ip6-config-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_ip6_config_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_IP6_CONFIG_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The specified property was not allowed in combination with the current 'method' */
+			ENUM_ENTRY (NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD, "NotAllowedForMethod"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingIP6ConfigError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING)
+
+#define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate))
+
+typedef struct {
+	char *method;
+	GSList *dns;        /* array of struct in6_addr */
+	GSList *dns_search; /* list of strings */
+	GSList *addresses;  /* array of NMIP6Address */
+	GSList *routes;     /* array of NMIP6Route */
+	gboolean ignore_auto_routes;
+	gboolean ignore_auto_dns;
+	gboolean never_default;
+	gboolean may_fail;
+} NMSettingIP6ConfigPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_METHOD,
+	PROP_DNS,
+	PROP_DNS_SEARCH,
+	PROP_ADDRESSES,
+	PROP_ROUTES,
+	PROP_IGNORE_AUTO_ROUTES,
+	PROP_IGNORE_AUTO_DNS,
+	PROP_NEVER_DEFAULT,
+	PROP_MAY_FAIL,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_ip6_config_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP6_CONFIG, NULL);
+}
+
+const char *
+nm_setting_ip6_config_get_method (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->method;
+}
+
+guint32
+nm_setting_ip6_config_get_num_dns (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns);
+}
+
+const struct in6_addr *
+nm_setting_ip6_config_get_dns (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->dns), NULL);
+
+	return (const struct in6_addr *) g_slist_nth_data (priv->dns, i);
+}
+
+gboolean
+nm_setting_ip6_config_add_dns (NMSettingIP6Config *setting, const struct in6_addr *addr)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	struct in6_addr *copy;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->dns; iter; iter = g_slist_next (iter)) {
+		if (!memcmp (addr, (struct in6_addr *) iter->data, sizeof (struct in6_addr)))
+			return FALSE;
+	}
+
+	copy = g_malloc0 (sizeof (struct in6_addr));
+	memcpy (copy, addr, sizeof (struct in6_addr));
+	priv->dns = g_slist_append (priv->dns, copy);
+
+	return TRUE;
+}
+
+void
+nm_setting_ip6_config_remove_dns (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->dns, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->dns = g_slist_delete_link (priv->dns, elt);
+}
+
+void
+nm_setting_ip6_config_clear_dns (NMSettingIP6Config *setting)
+{
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	nm_utils_slist_free (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns, g_free);
+	NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns = NULL;
+}
+
+guint32
+nm_setting_ip6_config_get_num_dns_searches (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search);
+}
+
+const char *
+nm_setting_ip6_config_get_dns_search (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->dns_search), NULL);
+
+	return (const char *) g_slist_nth_data (priv->dns_search, i);
+}
+
+gboolean
+nm_setting_ip6_config_add_dns_search (NMSettingIP6Config *setting,
+                                      const char *dns_search)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+	g_return_val_if_fail (dns_search != NULL, FALSE);
+	g_return_val_if_fail (dns_search[0] != '\0', FALSE);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (dns_search, (char *) iter->data))
+			return FALSE;
+	}
+
+	priv->dns_search = g_slist_append (priv->dns_search, g_strdup (dns_search));
+	return TRUE;
+}
+
+void
+nm_setting_ip6_config_remove_dns_search (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->dns_search, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->dns_search = g_slist_delete_link (priv->dns_search, elt);
+}
+
+void
+nm_setting_ip6_config_clear_dns_searches (NMSettingIP6Config *setting)
+{
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	nm_utils_slist_free (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search, g_free);
+	NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search = NULL;
+}
+
+guint32
+nm_setting_ip6_config_get_num_addresses (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->addresses);
+}
+
+NMIP6Address *
+nm_setting_ip6_config_get_address (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->addresses), NULL);
+
+	return (NMIP6Address *) g_slist_nth_data (priv->addresses, i);
+}
+
+gboolean
+nm_setting_ip6_config_add_address (NMSettingIP6Config *setting,
+                                   NMIP6Address *address)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	NMIP6Address *copy;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+	g_return_val_if_fail (address != NULL, FALSE);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
+		if (nm_ip6_address_compare ((NMIP6Address *) iter->data, address))
+			return FALSE;
+	}
+
+	copy = nm_ip6_address_dup (address);
+	g_return_val_if_fail (copy != NULL, FALSE);
+
+	priv->addresses = g_slist_append (priv->addresses, copy);
+	return TRUE;
+}
+
+void
+nm_setting_ip6_config_remove_address (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->addresses, i);
+	g_return_if_fail (elt != NULL);
+
+	nm_ip6_address_unref ((NMIP6Address *) elt->data);
+	priv->addresses = g_slist_delete_link (priv->addresses, elt);
+}
+
+void
+nm_setting_ip6_config_clear_addresses (NMSettingIP6Config *setting)
+{
+	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	nm_utils_slist_free (priv->addresses, (GDestroyNotify) nm_ip6_address_unref);
+	priv->addresses = NULL;
+}
+
+guint32
+nm_setting_ip6_config_get_num_routes (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+	return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->routes);
+}
+
+NMIP6Route *
+nm_setting_ip6_config_get_route (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->routes), NULL);
+
+	return (NMIP6Route *) g_slist_nth_data (priv->routes, i);
+}
+
+gboolean
+nm_setting_ip6_config_add_route (NMSettingIP6Config *setting,
+                                 NMIP6Route *route)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	NMIP6Route *copy;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+	g_return_val_if_fail (route != NULL, FALSE);
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	for (iter = priv->routes; iter; iter = g_slist_next (iter)) {
+		if (nm_ip6_route_compare ((NMIP6Route *) iter->data, route))
+			return FALSE;
+	}
+
+	copy = nm_ip6_route_dup (route);
+	g_return_val_if_fail (copy != NULL, FALSE);
+
+	priv->routes = g_slist_append (priv->routes, copy);
+	return TRUE;
+}
+
+void
+nm_setting_ip6_config_remove_route (NMSettingIP6Config *setting, guint32 i)
+{
+	NMSettingIP6ConfigPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->routes, i);
+	g_return_if_fail (elt != NULL);
+
+	nm_ip6_route_unref ((NMIP6Route *) elt->data);
+	priv->routes = g_slist_delete_link (priv->routes, elt);
+}
+
+void
+nm_setting_ip6_config_clear_routes (NMSettingIP6Config *setting)
+{
+	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+
+	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+	nm_utils_slist_free (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+	priv->routes = NULL;
+}
+
+gboolean
+nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ignore_auto_routes;
+}
+
+gboolean
+nm_setting_ip6_config_get_ignore_auto_dns (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ignore_auto_dns;
+}
+
+gboolean
+nm_setting_ip6_config_get_never_default (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->never_default;
+}
+
+gboolean
+nm_setting_ip6_config_get_may_fail (NMSettingIP6Config *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+	return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->may_fail;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+
+	if (!priv->method) {
+		g_set_error (error,
+		             NM_SETTING_IP6_CONFIG_ERROR,
+		             NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_IP6_CONFIG_METHOD);
+		return FALSE;
+	}
+
+	if (!strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		if (!priv->addresses) {
+			g_set_error (error,
+			             NM_SETTING_IP6_CONFIG_ERROR,
+			             NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY,
+			             NM_SETTING_IP6_CONFIG_ADDRESSES);
+			return FALSE;
+		}
+	} else if (   !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)
+	           || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)
+	           || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
+		if (g_slist_length (priv->dns)) {
+			g_set_error (error,
+			             NM_SETTING_IP6_CONFIG_ERROR,
+			             NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+			             NM_SETTING_IP6_CONFIG_DNS);
+			return FALSE;
+		}
+
+		if (g_slist_length (priv->dns_search)) {
+			g_set_error (error,
+			             NM_SETTING_IP6_CONFIG_ERROR,
+			             NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+			             NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+			return FALSE;
+		}
+
+		if (g_slist_length (priv->addresses)) {
+			g_set_error (error,
+			             NM_SETTING_IP6_CONFIG_ERROR,
+			             NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+			             NM_SETTING_IP6_CONFIG_ADDRESSES);
+			return FALSE;
+		}
+	} else if (!strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+		/* nothing to do */
+	} else {
+		g_set_error (error,
+		             NM_SETTING_IP6_CONFIG_ERROR,
+		             NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_IP6_CONFIG_METHOD);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+static void
+nm_setting_ip6_config_init (NMSettingIP6Config *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
+
+	g_free (priv->method);
+
+	if (priv->dns)
+		g_slist_free (priv->dns);
+
+	nm_utils_slist_free (priv->dns_search, g_free);
+	nm_utils_slist_free (priv->addresses, g_free);
+	nm_utils_slist_free (priv->routes, g_free);
+
+	G_OBJECT_CLASS (nm_setting_ip6_config_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_METHOD:
+		g_free (priv->method);
+		priv->method = g_value_dup_string (value);
+		break;
+	case PROP_DNS:
+		nm_utils_slist_free (priv->dns, g_free);
+		priv->dns = nm_utils_ip6_dns_from_gvalue (value);
+		break;
+	case PROP_DNS_SEARCH:
+		nm_utils_slist_free (priv->dns_search, g_free);
+		priv->dns_search = g_value_dup_boxed (value);
+		break;
+	case PROP_ADDRESSES:
+		nm_utils_slist_free (priv->addresses, g_free);
+		priv->addresses = nm_utils_ip6_addresses_from_gvalue (value);
+		break;
+	case PROP_ROUTES:
+		nm_utils_slist_free (priv->routes, g_free);
+		priv->routes = nm_utils_ip6_routes_from_gvalue (value);
+		break;
+	case PROP_IGNORE_AUTO_ROUTES:
+		priv->ignore_auto_routes = g_value_get_boolean (value);
+		break;
+	case PROP_IGNORE_AUTO_DNS:
+		priv->ignore_auto_dns = g_value_get_boolean (value);
+		break;
+	case PROP_NEVER_DEFAULT:
+		priv->never_default = g_value_get_boolean (value);
+		break;
+	case PROP_MAY_FAIL:
+		priv->may_fail = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_METHOD:
+		g_value_set_string (value, priv->method);
+		break;
+	case PROP_DNS:
+		nm_utils_ip6_dns_to_gvalue (priv->dns, value);
+		break;
+	case PROP_DNS_SEARCH:
+		g_value_set_boxed (value, priv->dns_search);
+		break;
+	case PROP_ADDRESSES:
+		nm_utils_ip6_addresses_to_gvalue (priv->addresses, value);
+		break;
+	case PROP_ROUTES:
+		nm_utils_ip6_routes_to_gvalue (priv->routes, value);
+		break;
+	case PROP_IGNORE_AUTO_ROUTES:
+		g_value_set_boolean (value, priv->ignore_auto_routes);
+		break;
+	case PROP_IGNORE_AUTO_DNS:
+		g_value_set_boolean (value, priv->ignore_auto_dns);
+		break;
+	case PROP_NEVER_DEFAULT:
+		g_value_set_boolean (value, priv->never_default);
+		break;
+	case PROP_MAY_FAIL:
+		g_value_set_boolean (value, priv->may_fail);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingIP6ConfigPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+	/**
+	 * NMSettingIP6Config:method:
+	 *
+	 * IPv6 configuration method.  If 'auto' is specified then the appropriate
+	 * automatic method (DHCP, PPP, router advertisement, etc) is used for the
+	 * interface and most other properties can be left unset.  If 'link-local'
+	 * is specified, then an IPv6 link-local address will be assigned to the
+	 * interface.  If 'manual' is specified, static IP addressing is used and
+	 * at least one IP address must be given in the 'addresses' property.  If
+	 * 'ignored' is specified, IPv6 configuration is not done. This property
+	 * must be set.  NOTE: the 'shared' method are not yet supported.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_METHOD,
+		 g_param_spec_string (NM_SETTING_IP6_CONFIG_METHOD,
+						      "Method",
+						      "IPv6 configuration method.  If 'auto' is specified "
+						      "then the appropriate automatic method (DHCP, PPP, "
+						      "router advertisement, etc) is used for the "
+						      "interface and most other properties can be left "
+						      "unset.  If 'link-local' is specified, then an "
+						      "IPv6 link-local address will be assigned to the "
+						      "interface.  If 'manual' is specified, static IP "
+						      "addressing is used and at least one IP address "
+						      "must be given in the 'addresses' property.  If "
+						      "'ignored' is specified, IPv6 configuration is not "
+						      "done. This property must be set.  NOTE: the "
+						      "'shared' method are not yet supported.",
+						      NULL,
+						      G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:dns:
+	 *
+	 * Array of DNS servers, where each member of the array is a byte array
+	 * containing the IPv6 address of the DNS server (in network byte order).
+	 * For the 'auto' method, these DNS servers are appended to those (if any)
+	 * returned by automatic configuration.  DNS servers cannot be used with
+	 * the 'shared' or 'link-local' methods as there is no usptream network. In
+	 * all other methods, these DNS servers are used as the only DNS servers for
+	 * this connection.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DNS,
+		 _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_DNS,
+							   "DNS",
+							   "Array of DNS servers, where each member of the "
+							   "array is a byte array containing the IPv6 address "
+							   "of the DNS server (in network byte order). For "
+							   "the 'auto' method, these DNS servers are "
+							   "appended to those (if any) returned by automatic "
+							   "configuration.  DNS servers cannot be used with "
+							   "the 'shared' or 'link-local' methods as there is "
+							   "no usptream network.  In all other methods, "
+							   "these DNS servers are used as the only DNS "
+							   "servers for this connection.",
+							   DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:dns-search:
+	 *
+	 * List of DNS search domains.  For the 'auto' method, these search domains
+	 * are appended to those returned by automatic configuration. Search domains
+	 * cannot be used with the 'shared' or 'link-local' methods as there is no
+	 * upstream network.  In all other methods, these search domains are used
+	 * as the only search domains for this connection.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DNS_SEARCH,
+		 _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_DNS_SEARCH,
+							   "DNS search",
+							   "List of DNS search domains.  For the 'auto' "
+							   "method, these search domains are appended to "
+							   "those returned by automatic configuration. "
+							   "Search domains cannot be used with the 'shared' "
+							   "or 'link-local' methods as there is no upstream "
+							   "network.  In all other methods, these search "
+							   "domains are used as the only search domains for "
+							   "this connection.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:addresses:
+	 *
+	 * Array of IPv6 address structures.  Each IPv6 address structure is
+	 * composed of 3 members, the first being a byte array containing the IPv6
+	 * address (network byte order), the second a 32-bit integer containing the
+	 * IPv6 address prefix, and the third a byte array containing the IPv6
+	 * address (network byte order) of the gateway associated with this address,
+	 * if any.  If no gateway is given, the third element should be given as
+	 * all zeros.  For the 'auto' method, given IP addresses are appended to
+	 * those returned by automatic configuration.  Addresses cannot be used with
+	 * the 'shared' or 'link-local' methods as the interface is automatically
+	 * assigned an address with these methods.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ADDRESSES,
+		 _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ADDRESSES,
+							   "Addresses",
+							   "Array of IPv6 address structures.  Each IPv6 "
+							   "address structure is composed of 3 members, the "
+							   "first being a byte array containing the IPv6 "
+							   "address (network byte order), the second a "
+							   "32-bit integer containing the IPv6 address "
+							   "prefix, and the third a byte array containing "
+							   "the IPv6 address (network byte order) of the "
+							   "gateway associated with this address, if any. "
+							   "If no gateway is given, the third element should "
+							   "be given as all zeros.  For the 'auto' method, "
+							   "given IP addresses are appended to those returned "
+							   "by automatic configuration.  Addresses cannot be "
+							   "used with the 'shared' or 'link-local' methods "
+							   "as the interface is automatically assigned an "
+							   "address with these methods.",
+							   DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:routes:
+	 *
+	 * Array of IPv6 route structures.  Each IPv6 route structure is composed
+	 * of 4 members; the first being the destination IPv6 network or
+	 * address (network byte order) as a byte array, the second the destination
+	 * network or address IPv6 prefix, the third being the next-hop IPv6 address
+	 * (network byte order) if any, and the fourth being the route metric. For
+	 * the 'auto' method, given IP routes are appended to those returned by
+	 * automatic configuration.  Routes cannot be used with the 'shared' or
+	 * 'link-local' methods because there is no upstream network.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_ROUTES,
+		 _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ROUTES,
+							   "Routes",
+							   "Array of IPv6 route structures.  Each IPv6 route "
+							   "structure is composed of 4 members; the first "
+							   "being the destination IPv6 network or address "
+							   "(network byte order) as a byte array, the second "
+							   "the destination network or address IPv6 prefix, "
+							   "the third being the next-hop IPv6 address "
+							   "(network byte order) if any, and the fourth "
+							   "being the route metric. For the 'auto' method, "
+							   "given IP routes are appended to those returned "
+							   "by automatic configuration.  Routes cannot be "
+							   "used with the 'shared' or 'link-local' methods "
+							   "because there is no upstream network.",
+							   DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:ignore-auto-routes:
+	 *
+	 * When the method is set to 'auto' and this property is set to TRUE,
+	 * automatically configured routes are ignored and only routes specified
+	 * in #NMSettingIP6Config:routes, if any, are used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_IGNORE_AUTO_ROUTES,
+		 g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
+						   "Ignore automatic routes",
+						   "When the method is set to 'auto' and this property "
+						   "is set to TRUE, automatically configured routes are "
+						   "ignored and only routes specified in the 'routes' "
+						   "property, if any, are used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:ignore-auto-dns:
+	 *
+	 * When the method is set to 'auto' and this property is set to TRUE,
+	 * automatically configured nameservers and search domains are ignored
+	 * and only nameservers and search domains specified in
+	 * #NMSettingIP6Config:dns and #NMSettingIP6Config:dns-search, if any, are
+	 * used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_IGNORE_AUTO_DNS,
+		 g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS,
+						   "Ignore DHCPv6/RDNSS DNS",
+						   "When the method is set to 'auto' and this property "
+						   "is set to TRUE, automatically configured nameservers "
+						   "and search domains are ignored and only nameservers "
+						   "and search domains specified in 'dns' and 'dns-search' "
+						   "properties, if any, are used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:never-default:
+	 *
+	 * If TRUE, this connection will never be the default IPv6 connection,
+	 * meaning it will never be assigned the default IPv6 route by NetworkManager.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NEVER_DEFAULT,
+		 g_param_spec_boolean (NM_SETTING_IP6_CONFIG_NEVER_DEFAULT,
+						   "Never default",
+						   "If TRUE, this connection will never be the default "
+						   "IPv6 connection, meaning it will never be assigned "
+						   "the default IPv6 route by NetworkManager.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingIP6Config:may-fail:
+	 *
+	 * If TRUE, allow overall network configuration to proceed even if IPv6
+	 * configuration times out.  Note that at least one IP configuration
+	 * must succeed or overall network configuration will still fail.  For
+	 * example, in IPv4-only networks, setting this property to TRUE allows
+	 * the overall network configuration to succeed if IPv6 configuration fails
+	 * but IPv4 configuration completes successfully.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MAY_FAIL,
+		 g_param_spec_boolean (NM_SETTING_IP6_CONFIG_MAY_FAIL,
+						   "May Fail",
+						   "If TRUE, allow overall network configuration to "
+						   "proceed even if IPv6 configuration times out. "
+						   "Note that at least one IP configuration must "
+						   "succeed or overall network configuration will still "
+						   "fail.  For example, in IPv4-only networks, setting "
+						   "this property to TRUE allows the overall network "
+						   "configuration to succeed if IPv6 configuration "
+						   "fails but IPv4 configuration completes successfully.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+}
+
+/********************************************************************/
+
+struct NMIP6Address {
+	guint32 refcount;
+	struct in6_addr address;
+	guint32 prefix;
+	struct in6_addr gateway;
+};
+
+NMIP6Address *
+nm_ip6_address_new (void)
+{
+	NMIP6Address *address;
+
+	address = g_malloc0 (sizeof (NMIP6Address));
+	address->refcount = 1;
+	return address;
+}
+
+NMIP6Address *
+nm_ip6_address_dup (NMIP6Address *source)
+{
+	NMIP6Address *address;
+
+	g_return_val_if_fail (source != NULL, NULL);
+	g_return_val_if_fail (source->refcount > 0, NULL);
+
+	address = nm_ip6_address_new ();
+	address->prefix = source->prefix;
+	memcpy (&address->address, &source->address, sizeof (struct in6_addr));
+	memcpy (&address->gateway, &source->gateway, sizeof (struct in6_addr));
+
+	return address;
+}
+
+void
+nm_ip6_address_ref (NMIP6Address *address)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->refcount++;
+}
+
+void
+nm_ip6_address_unref (NMIP6Address *address)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->refcount--;
+	if (address->refcount == 0) {
+		memset (address, 0, sizeof (NMIP6Address));
+		g_free (address);
+	}
+}
+
+gboolean
+nm_ip6_address_compare (NMIP6Address *address, NMIP6Address *other)
+{
+	g_return_val_if_fail (address != NULL, FALSE);
+	g_return_val_if_fail (address->refcount > 0, FALSE);
+
+	g_return_val_if_fail (other != NULL, FALSE);
+	g_return_val_if_fail (other->refcount > 0, FALSE);
+
+	if (   memcmp (&address->address, &other->address, sizeof (struct in6_addr))
+	    || address->prefix != other->prefix
+	    || memcmp (&address->gateway, &other->gateway, sizeof (struct in6_addr)))
+		return FALSE;
+	return TRUE;
+}
+
+const struct in6_addr *
+nm_ip6_address_get_address (NMIP6Address *address)
+{
+	g_return_val_if_fail (address != NULL, 0);
+	g_return_val_if_fail (address->refcount > 0, 0);
+
+	return &address->address;
+}
+
+void
+nm_ip6_address_set_address (NMIP6Address *address, const struct in6_addr *addr)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+	g_return_if_fail (addr != NULL);
+
+	memcpy (&address->address, addr, sizeof (struct in6_addr));
+}
+
+guint32
+nm_ip6_address_get_prefix (NMIP6Address *address)
+{
+	g_return_val_if_fail (address != NULL, 0);
+	g_return_val_if_fail (address->refcount > 0, 0);
+
+	return address->prefix;
+}
+
+void
+nm_ip6_address_set_prefix (NMIP6Address *address, guint32 prefix)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+
+	address->prefix = prefix;
+}
+
+const struct in6_addr *
+nm_ip6_address_get_gateway (NMIP6Address *address)
+{
+	g_return_val_if_fail (address != NULL, 0);
+	g_return_val_if_fail (address->refcount > 0, 0);
+
+	return &address->gateway;
+}
+
+void
+nm_ip6_address_set_gateway (NMIP6Address *address, const struct in6_addr *gw)
+{
+	g_return_if_fail (address != NULL);
+	g_return_if_fail (address->refcount > 0);
+	g_return_if_fail (gw != NULL);
+
+	memcpy (&address->gateway, gw, sizeof (struct in6_addr));
+}
+
+/********************************************************************/
+
+struct NMIP6Route {
+	guint32 refcount;
+
+	struct in6_addr dest;
+	guint32 prefix;
+	struct in6_addr next_hop;
+	guint32 metric;    /* lower metric == more preferred */
+};
+
+NMIP6Route *
+nm_ip6_route_new (void)
+{
+	NMIP6Route *route;
+
+	route = g_malloc0 (sizeof (NMIP6Route));
+	route->refcount = 1;
+	return route;
+}
+
+NMIP6Route *
+nm_ip6_route_dup (NMIP6Route *source)
+{
+	NMIP6Route *route;
+
+	g_return_val_if_fail (source != NULL, NULL);
+	g_return_val_if_fail (source->refcount > 0, NULL);
+
+	route = nm_ip6_route_new ();
+	route->prefix = source->prefix;
+	route->metric = source->metric;
+	memcpy (&route->dest, &source->dest, sizeof (struct in6_addr));
+	memcpy (&route->next_hop, &source->next_hop, sizeof (struct in6_addr));
+
+	return route;
+}
+
+void
+nm_ip6_route_ref (NMIP6Route *route)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->refcount++;
+}
+
+void
+nm_ip6_route_unref (NMIP6Route *route)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->refcount--;
+	if (route->refcount == 0) {
+		memset (route, 0, sizeof (NMIP6Route));
+		g_free (route);
+	}
+}
+
+gboolean
+nm_ip6_route_compare (NMIP6Route *route, NMIP6Route *other)
+{
+	g_return_val_if_fail (route != NULL, FALSE);
+	g_return_val_if_fail (route->refcount > 0, FALSE);
+
+	g_return_val_if_fail (other != NULL, FALSE);
+	g_return_val_if_fail (other->refcount > 0, FALSE);
+
+	if (   memcmp (&route->dest, &other->dest, sizeof (struct in6_addr))
+	    || route->prefix != other->prefix
+	    || memcmp (&route->next_hop, &other->next_hop, sizeof (struct in6_addr))
+	    || route->metric != other->metric)
+		return FALSE;
+	return TRUE;
+}
+
+const struct in6_addr *
+nm_ip6_route_get_dest (NMIP6Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return &route->dest;
+}
+
+void
+nm_ip6_route_set_dest (NMIP6Route *route, const struct in6_addr *dest)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+	g_return_if_fail (dest != NULL);
+
+	memcpy (&route->dest, dest, sizeof (struct in6_addr));
+}
+
+guint32
+nm_ip6_route_get_prefix (NMIP6Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return route->prefix;
+}
+
+void
+nm_ip6_route_set_prefix (NMIP6Route *route, guint32 prefix)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->prefix = prefix;
+}
+
+const struct in6_addr *
+nm_ip6_route_get_next_hop (NMIP6Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return &route->next_hop;
+}
+
+void
+nm_ip6_route_set_next_hop (NMIP6Route *route, const struct in6_addr *next_hop)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+	g_return_if_fail (next_hop != NULL);
+
+	memcpy (&route->next_hop, next_hop, sizeof (struct in6_addr));
+}
+
+guint32
+nm_ip6_route_get_metric (NMIP6Route *route)
+{
+	g_return_val_if_fail (route != NULL, 0);
+	g_return_val_if_fail (route->refcount > 0, 0);
+
+	return route->metric;
+}
+
+void
+nm_ip6_route_set_metric (NMIP6Route *route, guint32 metric)
+{
+	g_return_if_fail (route != NULL);
+	g_return_if_fail (route->refcount > 0);
+
+	route->metric = metric;
+}
diff --git a/libnm-util/nm-setting-ip6-config.h b/libnm-util/nm-setting-ip6-config.h
new file mode 100644
index 00000000..06e5c5c1
--- /dev/null
+++ b/libnm-util/nm-setting-ip6-config.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * David Cantrell <dcantrel@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_IP6_CONFIG_H
+#define NM_SETTING_IP6_CONFIG_H
+
+#include <arpa/inet.h>
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_IP6_CONFIG            (nm_setting_ip6_config_get_type ())
+#define NM_SETTING_IP6_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6Config))
+#define NM_SETTING_IP6_CONFIG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IP6CONFIG, NMSettingIP6ConfigClass))
+#define NM_IS_SETTING_IP6_CONFIG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_IP6_CONFIG))
+#define NM_IS_SETTING_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_IP6_CONFIG))
+#define NM_SETTING_IP6_CONFIG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigClass))
+
+#define NM_SETTING_IP6_CONFIG_SETTING_NAME "ipv6"
+
+typedef enum
+{
+	NM_SETTING_IP6_CONFIG_ERROR_UNKNOWN = 0,
+	NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY,
+	NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY,
+	NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD
+} NMSettingIP6ConfigError;
+
+#define NM_TYPE_SETTING_IP6_CONFIG_ERROR (nm_setting_ip6_config_error_get_type ()) 
+GType nm_setting_ip6_config_error_get_type (void);
+
+#define NM_SETTING_IP6_CONFIG_ERROR nm_setting_ip6_config_error_quark ()
+GQuark nm_setting_ip6_config_error_quark (void);
+
+#define NM_SETTING_IP6_CONFIG_METHOD             "method"
+#define NM_SETTING_IP6_CONFIG_DNS                "dns"
+#define NM_SETTING_IP6_CONFIG_DNS_SEARCH         "dns-search"
+#define NM_SETTING_IP6_CONFIG_ADDRESSES          "addresses"
+#define NM_SETTING_IP6_CONFIG_ROUTES             "routes"
+#define NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes"
+#define NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS    "ignore-auto-dns"
+#define NM_SETTING_IP6_CONFIG_NEVER_DEFAULT      "never-default"
+#define NM_SETTING_IP6_CONFIG_MAY_FAIL           "may-fail"
+
+#define NM_SETTING_IP6_CONFIG_METHOD_IGNORE     "ignore"
+#define NM_SETTING_IP6_CONFIG_METHOD_AUTO       "auto"
+#define NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL "link-local"
+#define NM_SETTING_IP6_CONFIG_METHOD_MANUAL     "manual"
+#define NM_SETTING_IP6_CONFIG_METHOD_SHARED     "shared"
+
+
+typedef struct NMIP6Address NMIP6Address;
+
+NMIP6Address *         nm_ip6_address_new         (void);
+NMIP6Address *         nm_ip6_address_dup         (NMIP6Address *source);
+void                   nm_ip6_address_ref         (NMIP6Address *address);
+void                   nm_ip6_address_unref       (NMIP6Address *address);
+/* Return TRUE if addresses are identical */
+gboolean               nm_ip6_address_compare     (NMIP6Address *address, NMIP6Address *other);
+
+const struct in6_addr *nm_ip6_address_get_address (NMIP6Address *address);
+void                   nm_ip6_address_set_address (NMIP6Address *address,
+                                                   const struct in6_addr *addr);
+
+guint32                nm_ip6_address_get_prefix  (NMIP6Address *address);
+void                   nm_ip6_address_set_prefix  (NMIP6Address *address,
+                                                   guint32 prefix);
+
+const struct in6_addr *nm_ip6_address_get_gateway (NMIP6Address *address);
+void                   nm_ip6_address_set_gateway (NMIP6Address *address,
+                                                   const struct in6_addr *gw);
+
+typedef struct NMIP6Route NMIP6Route;
+
+NMIP6Route *           nm_ip6_route_new          (void);
+NMIP6Route *           nm_ip6_route_dup          (NMIP6Route *route);
+void                   nm_ip6_route_ref          (NMIP6Route *route);
+void                   nm_ip6_route_unref        (NMIP6Route *route);
+/* Return TRUE if routes are identical */
+gboolean               nm_ip6_route_compare      (NMIP6Route *route, NMIP6Route *other);
+
+const struct in6_addr *nm_ip6_route_get_dest     (NMIP6Route *route);
+void                   nm_ip6_route_set_dest     (NMIP6Route *route,
+                                                  const struct in6_addr *dest);
+
+guint32                nm_ip6_route_get_prefix   (NMIP6Route *route);
+void                   nm_ip6_route_set_prefix   (NMIP6Route *route,
+                                        guint32 prefix);
+
+const struct in6_addr *nm_ip6_route_get_next_hop (NMIP6Route *route);
+void                   nm_ip6_route_set_next_hop (NMIP6Route *route,
+                                                  const struct in6_addr *next_hop);
+
+guint32                nm_ip6_route_get_metric   (NMIP6Route *route);
+void                   nm_ip6_route_set_metric   (NMIP6Route *route,
+                                                  guint32 metric);
+
+typedef struct {
+	NMSetting parent;
+} NMSettingIP6Config;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingIP6ConfigClass;
+
+GType nm_setting_ip6_config_get_type (void);
+
+NMSetting *            nm_setting_ip6_config_new                    (void);
+const char *           nm_setting_ip6_config_get_method             (NMSettingIP6Config *setting);
+
+guint32                nm_setting_ip6_config_get_num_dns            (NMSettingIP6Config *setting);
+const struct in6_addr *nm_setting_ip6_config_get_dns                (NMSettingIP6Config *setting, guint32 i);
+gboolean               nm_setting_ip6_config_add_dns                (NMSettingIP6Config *setting, const struct in6_addr *dns);
+void                   nm_setting_ip6_config_remove_dns             (NMSettingIP6Config *setting, guint32 i);
+void                   nm_setting_ip6_config_clear_dns              (NMSettingIP6Config *setting);
+
+guint32                nm_setting_ip6_config_get_num_dns_searches   (NMSettingIP6Config *setting);
+const char *           nm_setting_ip6_config_get_dns_search         (NMSettingIP6Config *setting, guint32 i);
+gboolean               nm_setting_ip6_config_add_dns_search         (NMSettingIP6Config *setting, const char *dns_search);
+void                   nm_setting_ip6_config_remove_dns_search      (NMSettingIP6Config *setting, guint32 i);
+void                   nm_setting_ip6_config_clear_dns_searches     (NMSettingIP6Config *setting);
+
+guint32                nm_setting_ip6_config_get_num_addresses      (NMSettingIP6Config *setting);
+NMIP6Address *         nm_setting_ip6_config_get_address            (NMSettingIP6Config *setting, guint32 i);
+gboolean               nm_setting_ip6_config_add_address            (NMSettingIP6Config *setting, NMIP6Address *address);
+void                   nm_setting_ip6_config_remove_address         (NMSettingIP6Config *setting, guint32 i);
+void                   nm_setting_ip6_config_clear_addresses        (NMSettingIP6Config *setting);
+
+guint32                nm_setting_ip6_config_get_num_routes         (NMSettingIP6Config *setting);
+NMIP6Route *           nm_setting_ip6_config_get_route              (NMSettingIP6Config *setting, guint32 i);
+gboolean               nm_setting_ip6_config_add_route              (NMSettingIP6Config *setting, NMIP6Route *route);
+void                   nm_setting_ip6_config_remove_route           (NMSettingIP6Config *setting, guint32 i);
+void                   nm_setting_ip6_config_clear_routes           (NMSettingIP6Config *setting);
+gboolean               nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting);
+
+gboolean               nm_setting_ip6_config_get_ignore_auto_dns    (NMSettingIP6Config *setting);
+gboolean               nm_setting_ip6_config_get_never_default      (NMSettingIP6Config *setting);
+gboolean               nm_setting_ip6_config_get_may_fail           (NMSettingIP6Config *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_IP6_CONFIG_H */
diff --git a/libnm-util/nm-setting-olpc-mesh.c b/libnm-util/nm-setting-olpc-mesh.c
new file mode 100644
index 00000000..1653bb07
--- /dev/null
+++ b/libnm-util/nm-setting-olpc-mesh.c
@@ -0,0 +1,296 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Daniel Drake <dsd@laptop.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ * (C) Copyright 2009 One Laptop per Child
+ */
+
+#include <string.h>
+#include <netinet/ether.h>
+#include <dbus/dbus-glib.h>
+
+#include "NetworkManager.h"
+#include "nm-setting-olpc-mesh.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-utils-private.h"
+
+GQuark
+nm_setting_olpc_mesh_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-olpc-mesh-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_olpc_mesh_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_OLPC_MESH_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingOlpcMeshError", values);
+	}
+	return etype;
+}
+
+static void nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting);
+
+G_DEFINE_TYPE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING)
+
+#define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate))
+
+typedef struct {
+	GByteArray *ssid;
+	guint32 channel;
+	GByteArray *dhcp_anycast_addr;
+} NMSettingOlpcMeshPrivate;
+
+enum {
+	PROP_0,
+	PROP_SSID,
+	PROP_CHANNEL,
+	PROP_DHCP_ANYCAST_ADDRESS,
+
+	LAST_PROP
+};
+
+/**
+ * nm_setting_olpc_mesh_new:
+ *
+ * Creates a new #NMSettingOlpcMesh object with default values.
+ *
+ * Returns: the new empty #NMSettingOlpcMesh object
+ **/
+NMSetting *nm_setting_olpc_mesh_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OLPC_MESH, NULL);
+}
+
+static void
+nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_OLPC_MESH_SETTING_NAME, NULL);
+}
+
+const GByteArray *
+nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL);
+
+	return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->ssid;
+}
+
+guint32
+nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), 0);
+
+	return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->channel;
+}
+
+const GByteArray *
+nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL);
+
+	return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->dhcp_anycast_addr;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (setting);
+
+	if (!priv->ssid) {
+		g_set_error (error,
+		             NM_SETTING_OLPC_MESH_ERROR,
+		             NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_OLPC_MESH_SSID);
+		return FALSE;
+	}
+
+	if (!priv->ssid->len || priv->ssid->len > 32) {
+		g_set_error (error,
+		             NM_SETTING_OLPC_MESH_ERROR,
+		             NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_OLPC_MESH_SSID);
+		return FALSE;
+	}
+
+	if (priv->channel == 0 || priv->channel > 13) {
+		g_set_error (error,
+		             NM_SETTING_OLPC_MESH_ERROR,
+		             NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_OLPC_MESH_CHANNEL);
+		return FALSE;
+	}
+
+	if (priv->dhcp_anycast_addr && priv->dhcp_anycast_addr->len != ETH_ALEN) {
+		g_set_error (error,
+		             NM_SETTING_OLPC_MESH_ERROR,
+		             NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object);
+
+	if (priv->ssid)
+		g_byte_array_free (priv->ssid, TRUE);
+	if (priv->dhcp_anycast_addr)
+		g_byte_array_free (priv->dhcp_anycast_addr, TRUE);
+
+	G_OBJECT_CLASS (nm_setting_olpc_mesh_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_SSID:
+		if (priv->ssid)
+			g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = g_value_dup_boxed (value);
+		break;
+	case PROP_CHANNEL:
+		priv->channel = g_value_get_uint (value);
+		break;
+	case PROP_DHCP_ANYCAST_ADDRESS:
+		if (priv->dhcp_anycast_addr)
+			g_byte_array_free (priv->dhcp_anycast_addr, TRUE);
+		priv->dhcp_anycast_addr = g_value_dup_boxed (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingOlpcMesh *setting = NM_SETTING_OLPC_MESH (object);
+
+	switch (prop_id) {
+	case PROP_SSID:
+		g_value_set_boxed (value, nm_setting_olpc_mesh_get_ssid (setting));
+		break;
+	case PROP_CHANNEL:
+		g_value_set_uint (value, nm_setting_olpc_mesh_get_channel (setting));
+		break;
+	case PROP_DHCP_ANYCAST_ADDRESS:
+		g_value_set_boxed (value, nm_setting_olpc_mesh_get_dhcp_anycast_address (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingOlpcMeshPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+	/**
+	 * NMSettingOlpcMesh:ssid:
+	 *
+	 * SSID of the mesh network to join.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SSID,
+		 _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_SSID,
+		                             "SSID",
+		                             "SSID of the mesh network to join.",
+		                             DBUS_TYPE_G_UCHAR_ARRAY,
+		                             G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingOlpcMesh:channel:
+	 *
+	 * Channel on which the mesh network to join is located.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_CHANNEL,
+		 g_param_spec_uint (NM_SETTING_OLPC_MESH_CHANNEL,
+		                    "Channel",
+		                    "Channel on which the mesh network to join is located.",
+		                    0, G_MAXUINT32, 0,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingOlpcMesh:dhcp-anycast-address:
+	 *
+	 * Anycast DHCP address used when requesting an IP address via DHCP.  The
+	 * specific anycast address used determines which DHCP server class answers
+	 * the request.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DHCP_ANYCAST_ADDRESS,
+		 _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS,
+		                             "Anycast DHCP MAC address",
+		                             "Anycast DHCP MAC address used when "
+		                             "requesting an IP address via DHCP.  The "
+		                             "specific anycast address used determines "
+		                             "which DHCP server class answers the "
+		                             "the request.",
+		                             DBUS_TYPE_G_UCHAR_ARRAY,
+		                             G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+}
diff --git a/libnm-util/nm-setting-olpc-mesh.h b/libnm-util/nm-setting-olpc-mesh.h
new file mode 100644
index 00000000..84abd126
--- /dev/null
+++ b/libnm-util/nm-setting-olpc-mesh.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Daniel Drake <dsd@laptop.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ * (C) Copyright 2009 One Laptop per Child
+ */
+
+#ifndef NM_SETTING_OLPC_MESH_H
+#define NM_SETTING_OLPC_MESH_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OLPC_MESH            (nm_setting_olpc_mesh_get_type ())
+#define NM_SETTING_OLPC_MESH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMesh))
+#define NM_SETTING_OLPC_MESH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass))
+#define NM_IS_SETTING_OLPC_MESH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OLPC_MESH))
+#define NM_IS_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_OLPC_MESH))
+#define NM_SETTING_OLPC_MESH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass))
+
+#define NM_SETTING_OLPC_MESH_SETTING_NAME "802-11-olpc-mesh"
+
+typedef enum
+{
+	NM_SETTING_OLPC_MESH_ERROR_UNKNOWN = 0,
+	NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+	NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY
+} NMSettingOlpcMeshError;
+
+#define NM_TYPE_SETTING_OLPC_MESH_ERROR (nm_setting_olpc_mesh_error_get_type ()) 
+GType nm_setting_olpc_mesh_error_get_type (void);
+
+#define NM_SETTING_OLPC_MESH_ERROR nm_setting_olpc_mesh_error_quark ()
+GQuark nm_setting_olpc_mesh_error_quark (void);
+
+#define NM_SETTING_OLPC_MESH_SSID                 "ssid"
+#define NM_SETTING_OLPC_MESH_CHANNEL              "channel"
+#define NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS "dhcp-anycast-address"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingOlpcMesh;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingOlpcMeshClass;
+
+GType nm_setting_olpc_mesh_get_type (void);
+
+NMSetting *       nm_setting_olpc_mesh_new                      (void);
+const GByteArray *nm_setting_olpc_mesh_get_ssid                 (NMSettingOlpcMesh *setting);
+guint32           nm_setting_olpc_mesh_get_channel              (NMSettingOlpcMesh *setting);
+const GByteArray *nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_OLPC_MESH_H */
diff --git a/libnm-util/nm-setting-ppp.c b/libnm-util/nm-setting-ppp.c
new file mode 100644
index 00000000..fd6043f1
--- /dev/null
+++ b/libnm-util/nm-setting-ppp.c
@@ -0,0 +1,730 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include "nm-setting-ppp.h"
+
+GQuark
+nm_setting_ppp_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-ppp-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_ppp_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_PPP_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_PPP_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_PPP_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The 'require-mppe' option is not allowed in conjunction with the specified authentication. */
+			ENUM_ENTRY (NM_SETTING_PPP_ERROR_REQUIRE_MPPE_NOT_ALLOWED, "RequireMPPENotAllowed"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingPPPError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingPPP, nm_setting_ppp, NM_TYPE_SETTING)
+
+#define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPPPPrivate))
+
+typedef struct {
+	gboolean noauth;
+	gboolean refuse_eap;
+	gboolean refuse_pap;
+	gboolean refuse_chap;
+	gboolean refuse_mschap;
+	gboolean refuse_mschapv2;
+	gboolean nobsdcomp;
+	gboolean nodeflate;
+	gboolean no_vj_comp;
+	gboolean require_mppe;
+	gboolean require_mppe_128;
+	gboolean mppe_stateful;
+	gboolean crtscts;
+	guint32 baud;
+	guint32 mru;
+	guint32 mtu;
+	guint32 lcp_echo_failure;
+	guint32 lcp_echo_interval;
+} NMSettingPPPPrivate;
+
+enum {
+	PROP_0,
+	PROP_NOAUTH,
+	PROP_REFUSE_EAP,
+	PROP_REFUSE_PAP,
+	PROP_REFUSE_CHAP,
+	PROP_REFUSE_MSCHAP,
+	PROP_REFUSE_MSCHAPV2,
+	PROP_NOBSDCOMP,
+	PROP_NODEFLATE,
+	PROP_NO_VJ_COMP,
+	PROP_REQUIRE_MPPE,
+	PROP_REQUIRE_MPPE_128,
+	PROP_MPPE_STATEFUL,
+	PROP_CRTSCTS,
+	PROP_BAUD,
+	PROP_MRU,
+	PROP_MTU,
+	PROP_LCP_ECHO_FAILURE,
+	PROP_LCP_ECHO_INTERVAL,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_ppp_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPP, NULL);
+}
+
+gboolean
+nm_setting_ppp_get_noauth (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->noauth;
+}
+
+gboolean
+nm_setting_ppp_get_refuse_eap (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_eap;
+}
+
+gboolean
+nm_setting_ppp_get_refuse_pap (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_pap;
+}
+
+gboolean
+nm_setting_ppp_get_refuse_chap (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_chap;
+}
+
+gboolean
+nm_setting_ppp_get_refuse_mschap (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_mschap;
+}
+
+gboolean
+nm_setting_ppp_get_refuse_mschapv2 (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_mschapv2;
+}
+
+gboolean
+nm_setting_ppp_get_nobsdcomp (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->nobsdcomp;
+}
+
+gboolean
+nm_setting_ppp_get_nodeflate (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->nodeflate;
+}
+
+gboolean
+nm_setting_ppp_get_no_vj_comp (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->no_vj_comp;
+}
+
+gboolean
+nm_setting_ppp_get_require_mppe (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->require_mppe;
+}
+
+gboolean
+nm_setting_ppp_get_require_mppe_128 (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->require_mppe_128;
+}
+
+gboolean
+nm_setting_ppp_get_mppe_stateful (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->mppe_stateful;
+}
+
+gboolean
+nm_setting_ppp_get_crtscts (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->crtscts;
+}
+
+guint32
+nm_setting_ppp_get_baud (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->baud;
+}
+
+guint32
+nm_setting_ppp_get_mru (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->mru;
+}
+
+guint32
+nm_setting_ppp_get_mtu (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->mtu;
+}
+
+guint32
+nm_setting_ppp_get_lcp_echo_failure (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->lcp_echo_failure;
+}
+
+guint32
+nm_setting_ppp_get_lcp_echo_interval (NMSettingPPP *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+	return NM_SETTING_PPP_GET_PRIVATE (setting)->lcp_echo_interval;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingPPPPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (setting);
+
+	/* FIXME: Do we even want this or can we just let pppd evaluate the options? */
+	if (priv->mru > 0) {
+		if (priv->mru < 128 || priv->mru > 16384) {
+			g_set_error (error,
+			             NM_SETTING_PPP_ERROR,
+			             NM_SETTING_PPP_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_PPP_MRU);
+			return FALSE;
+		}
+	}
+
+	if (priv->lcp_echo_failure > 0) {
+		/* lcp_echo_interval must also be non-zero */
+		if (priv->lcp_echo_interval == 0) {
+			g_set_error (error,
+			             NM_SETTING_PPP_ERROR,
+			             NM_SETTING_PPP_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_PPP_LCP_ECHO_FAILURE);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_ppp_init (NMSettingPPP *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_PPP_SETTING_NAME, NULL);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingPPPPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_NOAUTH:
+		priv->noauth = g_value_get_boolean (value);
+		break;
+	case PROP_REFUSE_EAP:
+		priv->refuse_eap = g_value_get_boolean (value);
+		break;
+	case PROP_REFUSE_PAP:
+		priv->refuse_pap = g_value_get_boolean (value);
+		break;
+	case PROP_REFUSE_CHAP:
+		priv->refuse_chap = g_value_get_boolean (value);
+		break;
+	case PROP_REFUSE_MSCHAP:
+		priv->refuse_mschap = g_value_get_boolean (value);
+		break;
+	case PROP_REFUSE_MSCHAPV2:
+		priv->refuse_mschapv2 = g_value_get_boolean (value);
+		break;
+	case PROP_NOBSDCOMP:
+		priv->nobsdcomp = g_value_get_boolean (value);
+		break;
+	case PROP_NODEFLATE:
+		priv->nodeflate = g_value_get_boolean (value);
+		break;
+	case PROP_NO_VJ_COMP:
+		priv->no_vj_comp = g_value_get_boolean (value);
+		break;
+	case PROP_REQUIRE_MPPE:
+		priv->require_mppe = g_value_get_boolean (value);
+		break;
+	case PROP_REQUIRE_MPPE_128:
+		priv->require_mppe_128 = g_value_get_boolean (value);
+		break;
+	case PROP_MPPE_STATEFUL:
+		priv->mppe_stateful = g_value_get_boolean (value);
+		break;
+	case PROP_CRTSCTS:
+		priv->crtscts = g_value_get_boolean (value);
+		break;
+	case PROP_BAUD:
+		priv->baud = g_value_get_uint (value);
+		break;
+	case PROP_MRU:
+		priv->mru = g_value_get_uint (value);
+		break;
+	case PROP_MTU:
+		priv->mtu = g_value_get_uint (value);
+		break;
+	case PROP_LCP_ECHO_FAILURE:
+		priv->lcp_echo_failure = g_value_get_uint (value);
+		break;
+	case PROP_LCP_ECHO_INTERVAL:
+		priv->lcp_echo_interval = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingPPP *setting = NM_SETTING_PPP (object);
+
+	switch (prop_id) {
+	case PROP_NOAUTH:
+		g_value_set_boolean (value, nm_setting_ppp_get_noauth (setting));
+		break;
+	case PROP_REFUSE_EAP:
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_eap (setting));
+		break;
+	case PROP_REFUSE_PAP:
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_pap (setting));
+		break;
+	case PROP_REFUSE_CHAP:
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_chap (setting));
+		break;
+	case PROP_REFUSE_MSCHAP:
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschap (setting));
+		break;
+	case PROP_REFUSE_MSCHAPV2:
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschapv2 (setting));
+		break;
+	case PROP_NOBSDCOMP:
+		g_value_set_boolean (value, nm_setting_ppp_get_nobsdcomp (setting));
+		break;
+	case PROP_NODEFLATE:
+		g_value_set_boolean (value, nm_setting_ppp_get_nodeflate (setting));
+		break;
+	case PROP_NO_VJ_COMP:
+		g_value_set_boolean (value, nm_setting_ppp_get_no_vj_comp (setting));
+		break;
+	case PROP_REQUIRE_MPPE:
+		g_value_set_boolean (value, nm_setting_ppp_get_require_mppe (setting));
+		break;
+	case PROP_REQUIRE_MPPE_128:
+		g_value_set_boolean (value, nm_setting_ppp_get_require_mppe_128 (setting));
+		break;
+	case PROP_MPPE_STATEFUL:
+		g_value_set_boolean (value, nm_setting_ppp_get_mppe_stateful (setting));
+		break;
+	case PROP_CRTSCTS:
+		g_value_set_boolean (value, nm_setting_ppp_get_crtscts (setting));
+		break;
+	case PROP_BAUD:
+		g_value_set_uint (value, nm_setting_ppp_get_baud (setting));
+		break;
+	case PROP_MRU:
+		g_value_set_uint (value, nm_setting_ppp_get_mru (setting));
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_ppp_get_mtu (setting));
+		break;
+	case PROP_LCP_ECHO_FAILURE:
+		g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_failure (setting));
+		break;
+	case PROP_LCP_ECHO_INTERVAL:
+		g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_interval (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_ppp_class_init (NMSettingPPPClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingPPPPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	parent_class->verify       = verify;
+
+	/* Properties */
+	/**
+	 * NMSettingPPP:noauth:
+	 *
+	 * If TRUE, do not require the other side (usually the PPP server) to
+	 * authenticate itself to the client.  If FALSE, require authentication from
+	 * the remote side.  In almost all cases, this should be TRUE.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NOAUTH,
+		 g_param_spec_boolean (NM_SETTING_PPP_NOAUTH,
+						   "NoAuth",
+						   "If TRUE, do not require the other side (usually the "
+						   "PPP server) to authenticate itself to the client.  "
+						   "If FALSE, require authentication from the remote "
+						   "side.  In almost all cases, this should be TRUE.",
+						   TRUE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:refuse-eap:
+	 *
+	 * If TRUE, the EAP authentication method will not be used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REFUSE_EAP,
+		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_EAP,
+						   "Refuse EAP",
+						   "If TRUE, the EAP authentication method will not be used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:refuse-pap:
+	 *
+	 * If TRUE, the PAP authentication method will not be used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REFUSE_PAP,
+		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_PAP,
+						   "Refuse PAP",
+						   "If TRUE, the PAP authentication method will not be used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:refuse-chap:
+	 *
+	 * If TRUE, the CHAP authentication method will not be used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REFUSE_CHAP,
+		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_CHAP,
+						   "Refuse CHAP",
+						   "If TRUE, the CHAP authentication method will not be used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:refuse-mschap:
+	 *
+	 * If TRUE, the MSCHAP authentication method will not be used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REFUSE_MSCHAP,
+		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAP,
+						   "Refuse MSCHAP",
+						   "If TRUE, the MSCHAP authentication method will not be used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:refuse-mschapv2:
+	 *
+	 * If TRUE, the MSCHAPv2 authentication method will not be used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REFUSE_MSCHAPV2,
+		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAPV2,
+						   "Refuse MSCHAPv2",
+						   "If TRUE, the MSCHAPv2 authentication method will not be used.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:nobsdcomp:
+	 *
+	 * If TRUE, BSD compression will not be requested.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NOBSDCOMP,
+		 g_param_spec_boolean (NM_SETTING_PPP_NOBSDCOMP,
+						   "No BSD compression",
+						   "If TRUE, BSD compression will not be requested.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingPPP:nodeflate:
+	 *
+	 * If TRUE, 'deflate' compression will not be requested.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NODEFLATE,
+		 g_param_spec_boolean (NM_SETTING_PPP_NODEFLATE,
+						   "No deflate",
+						   "If TRUE, 'deflate' compression will not be requested.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingPPP:no-vj-comp:
+	 *
+	 * If TRUE, Van Jacobsen TCP header compression will not be requested.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NO_VJ_COMP,
+		 g_param_spec_boolean (NM_SETTING_PPP_NO_VJ_COMP,
+						   "No VJ compression",
+						   "If TRUE, Van Jacobsen TCP header compression will not be requested.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingPPP:require-mppe:
+	 *
+	 * If TRUE, MPPE (Microsoft Point-to-Point Encrpytion) will be required for
+	 * the PPP session.  If either 64-bit or 128-bit MPPE is not available the
+	 * session will fail.  Note that MPPE is not used on mobile broadband
+	 * connections.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REQUIRE_MPPE,
+		 g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE,
+						   "Require MPPE",
+						   "If TRUE, MPPE (Microsoft Point-to-Point Encrpytion) "
+						   "will be required for the PPP session.  If either "
+						   "64-bit or 128-bit MPPE is not available the session "
+						   "will fail.  Note that MPPE is not used on mobile "
+						   "broadband connections.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:require-mppe-128:
+	 *
+	 * If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encrpytion) will be
+	 * required for the PPP session, and the 'require-mppe' property must also
+	 * be set to TRUE.  If 128-bit MPPE is not available the session will fail.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_REQUIRE_MPPE_128,
+		 g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE_128,
+						   "Require MPPE 128",
+						   "If TRUE, 128-bit MPPE (Microsoft Point-to-Point "
+						   "Encrpytion) will be required for the PPP session, "
+						   "and the '" NM_SETTING_PPP_REQUIRE_MPPE "' property "
+						   "must also be set to TRUE.  If 128-bit MPPE is not "
+						   "available the session will fail.",
+						   FALSE,
+						   G_PARAM_READWRITE  | G_PARAM_CONSTRUCT| NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:mppe-stateful:
+	 *
+	 * If TRUE, stateful MPPE is used.  See pppd documentation for more
+	 * information on stateful MPPE.",
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MPPE_STATEFUL,
+		 g_param_spec_boolean (NM_SETTING_PPP_MPPE_STATEFUL,
+						   "MPPE stateful",
+						   "If TRUE, stateful MPPE is used.  See pppd documentation "
+						   "for more information on stateful MPPE.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:crtscts:
+	 *
+	 * If TRUE, specify that pppd should set the serial port to use hardware
+	 * flow control with RTS and CTS signals.  This value should normally be
+	 * set to FALSE.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_CRTSCTS,
+		 g_param_spec_boolean (NM_SETTING_PPP_CRTSCTS,
+						   "CRTSCTS",
+						   "If TRUE, specify that pppd should set the serial "
+						   "port to use hardware flow control with RTS and CTS "
+						   "signals.  This value should normally be set to "
+						   "FALSE.",
+						   FALSE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:baud:
+	 *
+	 * If non-zero, instruct pppd to set the serial port to the specified
+	 * baudrate.  This value should normally be left as 0 to automatically
+	 * choose the speed.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BAUD,
+		 g_param_spec_uint (NM_SETTING_PPP_BAUD,
+						"Baud",
+						"If non-zero, instruct pppd to set the serial port to "
+						"the specified baudrate.  This value should normally be "
+						"left as 0 to automatically choose the speed.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingPPP:mru:
+	 *
+	 * If non-zero, instruct pppd to request that the peer send packets no
+	 * larger than the specified size.  If non-zero, the MRU should be between
+	 * 128 and 16384.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MRU,
+		 g_param_spec_uint (NM_SETTING_PPP_MRU,
+						"MRU",
+						"If non-zero, instruct pppd to request that the peer "
+						"send packets no larger than the specified size.  If "
+						"non-zero, the MRU should be between 128 and 16384.",
+						0, 16384, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPP:mtu:
+	 *
+	 * If non-zero, instruct pppd to send packets no larger than the specified
+	 * size.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MTU,
+		 g_param_spec_uint (NM_SETTING_PPP_MTU,
+						"MTU",
+						"If non-zero, instruct pppd to send packets no larger "
+						"than the specified size.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingPPP:lcp-echo-failure:
+	 *
+	 * If non-zero, instruct pppd to presume the connection to the peer has
+	 * failed if the specified number of LCP echo-requests go unanswered by the
+	 * peer.  The 'lcp-echo-interval' property must also be set to a non-zero
+	 * value if this property is used.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_LCP_ECHO_FAILURE,
+		 g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_FAILURE,
+						"LCP echo failure",
+						"If non-zero, instruct pppd to presume the connection to "
+						"the peer has failed if the specified number of LCP "
+						"echo-requests go unanswered by the peer.  The "
+						"'" NM_SETTING_PPP_LCP_ECHO_INTERVAL "' property must "
+						"also be set to a non-zero value if this property is "
+						"used.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingPPP:lcp-echo-interval:
+	 *
+	 * If non-zero, instruct pppd to send an LCP echo-request frame to the peer
+	 * every n seconds (where n is the specified value).  Note that some PPP
+	 * peers will respond to echo requests and some will not, and it is not
+	 * possible to autodetect this.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_LCP_ECHO_INTERVAL,
+		 g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_INTERVAL,
+						"LCP echo interval",
+						"If non-zero, instruct pppd to send an LCP echo-request "
+						"frame to the peer every n seconds (where n is the "
+						"specified value).  Note that some PPP peers will respond "
+						"to echo requests and some will not, and it is not "
+						"possible to autodetect this.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+}
diff --git a/libnm-util/nm-setting-ppp.h b/libnm-util/nm-setting-ppp.h
new file mode 100644
index 00000000..cfa1f57a
--- /dev/null
+++ b/libnm-util/nm-setting-ppp.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_PPP_H
+#define NM_SETTING_PPP_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_PPP            (nm_setting_ppp_get_type ())
+#define NM_SETTING_PPP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_PPP, NMSettingPPP))
+#define NM_SETTING_PPP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_PPP, NMSettingPPPClass))
+#define NM_IS_SETTING_PPP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_PPP))
+#define NM_IS_SETTING_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_PPP))
+#define NM_SETTING_PPP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_PPP, NMSettingPPPClass))
+
+#define NM_SETTING_PPP_SETTING_NAME "ppp"
+
+typedef enum
+{
+	NM_SETTING_PPP_ERROR_UNKNOWN = 0,
+	NM_SETTING_PPP_ERROR_INVALID_PROPERTY,
+	NM_SETTING_PPP_ERROR_MISSING_PROPERTY,
+	NM_SETTING_PPP_ERROR_REQUIRE_MPPE_NOT_ALLOWED
+} NMSettingPPPError;
+
+#define NM_TYPE_SETTING_PPP_ERROR (nm_setting_ppp_error_get_type ()) 
+GType nm_setting_ppp_error_get_type (void);
+
+#define NM_SETTING_PPP_ERROR nm_setting_ppp_error_quark ()
+GQuark nm_setting_ppp_error_quark (void);
+
+#define NM_SETTING_PPP_NOAUTH            "noauth"
+#define NM_SETTING_PPP_REFUSE_EAP        "refuse-eap"
+#define NM_SETTING_PPP_REFUSE_PAP        "refuse-pap"
+#define NM_SETTING_PPP_REFUSE_CHAP       "refuse-chap"
+#define NM_SETTING_PPP_REFUSE_MSCHAP     "refuse-mschap"
+#define NM_SETTING_PPP_REFUSE_MSCHAPV2   "refuse-mschapv2"
+#define NM_SETTING_PPP_NOBSDCOMP         "nobsdcomp"
+#define NM_SETTING_PPP_NODEFLATE         "nodeflate"
+#define NM_SETTING_PPP_NO_VJ_COMP        "no-vj-comp"
+#define NM_SETTING_PPP_REQUIRE_MPPE      "require-mppe"
+#define NM_SETTING_PPP_REQUIRE_MPPE_128  "require-mppe-128"
+#define NM_SETTING_PPP_MPPE_STATEFUL     "mppe-stateful"
+#define NM_SETTING_PPP_CRTSCTS           "crtscts"
+#define NM_SETTING_PPP_BAUD              "baud"
+#define NM_SETTING_PPP_MRU               "mru"
+#define NM_SETTING_PPP_MTU               "mtu"
+#define NM_SETTING_PPP_LCP_ECHO_FAILURE  "lcp-echo-failure"
+#define NM_SETTING_PPP_LCP_ECHO_INTERVAL "lcp-echo-interval"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingPPP;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingPPPClass;
+
+GType nm_setting_ppp_get_type (void);
+
+NMSetting *nm_setting_ppp_new                   (void);
+gboolean   nm_setting_ppp_get_noauth            (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_refuse_eap        (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_refuse_pap        (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_refuse_chap       (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_refuse_mschap     (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_refuse_mschapv2   (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_nobsdcomp         (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_nodeflate         (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_no_vj_comp        (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_require_mppe      (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_require_mppe_128  (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_mppe_stateful     (NMSettingPPP *setting);
+gboolean   nm_setting_ppp_get_crtscts           (NMSettingPPP *setting);
+guint32    nm_setting_ppp_get_baud              (NMSettingPPP *setting);
+guint32    nm_setting_ppp_get_mru               (NMSettingPPP *setting);
+guint32    nm_setting_ppp_get_mtu               (NMSettingPPP *setting);
+guint32    nm_setting_ppp_get_lcp_echo_failure  (NMSettingPPP *setting);
+guint32    nm_setting_ppp_get_lcp_echo_interval (NMSettingPPP *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_PPP_H */
diff --git a/libnm-util/nm-setting-pppoe.c b/libnm-util/nm-setting-pppoe.c
new file mode 100644
index 00000000..d1aba43d
--- /dev/null
+++ b/libnm-util/nm-setting-pppoe.c
@@ -0,0 +1,274 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2010 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include "nm-setting-pppoe.h"
+#include "nm-setting-ppp.h"
+
+GQuark
+nm_setting_pppoe_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-pppoe-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_pppoe_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_PPPOE_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The required PPP setting is missing */
+			ENUM_ENTRY (NM_SETTING_PPPOE_ERROR_MISSING_PPP_SETTING, "MissingPPPSetting"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingPPPOEError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingPPPOE, nm_setting_pppoe, NM_TYPE_SETTING)
+
+#define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEPrivate))
+
+typedef struct {
+	char *service;
+	char *username;
+	char *password;
+} NMSettingPPPOEPrivate;
+
+enum {
+	PROP_0,
+	PROP_SERVICE,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_pppoe_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPPOE, NULL);
+}
+
+const char *
+nm_setting_pppoe_get_service  (NMSettingPPPOE *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+	return NM_SETTING_PPPOE_GET_PRIVATE (setting)->service;
+}
+
+const char *
+nm_setting_pppoe_get_username (NMSettingPPPOE *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+	return NM_SETTING_PPPOE_GET_PRIVATE (setting)->username;
+}
+
+const char *
+nm_setting_pppoe_get_password (NMSettingPPPOE *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+	return NM_SETTING_PPPOE_GET_PRIVATE (setting)->password;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting);
+
+	if (!priv->username) {
+		g_set_error (error,
+		             NM_SETTING_PPPOE_ERROR,
+		             NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_PPPOE_USERNAME);
+		return FALSE;
+	} else if (!strlen (priv->username)) {
+		g_set_error (error,
+		             NM_SETTING_PPPOE_ERROR,
+		             NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_PPPOE_USERNAME);
+		return FALSE;
+	}
+
+	if (priv->service && !strlen (priv->service)) {
+		g_set_error (error,
+		             NM_SETTING_PPPOE_ERROR,
+		             NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_PPPOE_SERVICE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+	NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting);
+	GPtrArray *secrets;
+
+	if (priv->password)
+		return NULL;
+
+	secrets = g_ptr_array_sized_new (1);
+	g_ptr_array_add (secrets, NM_SETTING_PPPOE_PASSWORD);
+
+	return secrets;
+}
+
+static void
+nm_setting_pppoe_init (NMSettingPPPOE *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_PPPOE_SETTING_NAME, NULL);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_SERVICE:
+		g_free (priv->service);
+		priv->service = g_value_dup_string (value);
+		break;
+	case PROP_USERNAME:
+		g_free (priv->username);
+		priv->username = g_value_dup_string (value);
+		break;
+	case PROP_PASSWORD:
+		g_free (priv->password);
+		priv->password = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingPPPOE *setting = NM_SETTING_PPPOE (object);
+
+	switch (prop_id) {
+	case PROP_SERVICE:
+		g_value_set_string (value, nm_setting_pppoe_get_service (setting));
+		break;
+	case PROP_USERNAME:
+		g_value_set_string (value, nm_setting_pppoe_get_username (setting));
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nm_setting_pppoe_get_password (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_pppoe_class_init (NMSettingPPPOEClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingPPPOEPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	parent_class->verify       = verify;
+	parent_class->need_secrets = need_secrets;
+
+	/* Properties */
+	/**
+	 * NMSettingPPPOE:service:
+	 *
+	 * If specified, instruct PPPoE to only initiate sessions with access
+	 * concentrators that provide the specified serivce.  For most providers,
+	 * this should be left blank.  It is only required if there are multiple
+	 * access concentrators or a specific service is known to be required.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SERVICE,
+		 g_param_spec_string (NM_SETTING_PPPOE_SERVICE,
+						  "Service",
+						  "If specified, instruct PPPoE to only initiate sessions "
+						  "with access concentrators that provide the specified "
+						  "serivce.  For most providers, this should be left "
+						  "blank.  It is only required if there are multiple "
+						  "access concentrators or a specific service is known "
+						  "to be required.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPPOE:username:
+	 *
+	 * Username used to authenticate with the PPPoE service.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_USERNAME,
+		 g_param_spec_string (NM_SETTING_PPPOE_USERNAME,
+						  "Username",
+						  "Username used to authenticate with the PPPoE service.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingPPPOE:password:
+	 *
+	 * Password used to authenticate with the PPPoE service.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PASSWORD,
+		 g_param_spec_string (NM_SETTING_PPPOE_PASSWORD,
+						  "Password",
+						  "Password used to authenticate with the PPPoE service.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+}
diff --git a/libnm-util/nm-setting-pppoe.h b/libnm-util/nm-setting-pppoe.h
new file mode 100644
index 00000000..83e95d4e
--- /dev/null
+++ b/libnm-util/nm-setting-pppoe.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_PPPOE_H
+#define NM_SETTING_PPPOE_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_PPPOE            (nm_setting_pppoe_get_type ())
+#define NM_SETTING_PPPOE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_PPPOE, NMSettingPPPOE))
+#define NM_SETTING_PPPOE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEClass))
+#define NM_IS_SETTING_PPPOE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_PPPOE))
+#define NM_IS_SETTING_PPPOE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_PPPOE))
+#define NM_SETTING_PPPOE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEClass))
+
+#define NM_SETTING_PPPOE_SETTING_NAME "pppoe"
+
+typedef enum
+{
+	NM_SETTING_PPPOE_ERROR_UNKNOWN = 0,
+	NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY,
+	NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY,
+	NM_SETTING_PPPOE_ERROR_MISSING_PPP_SETTING
+} NMSettingPPPOEError;
+
+#define NM_TYPE_SETTING_PPPOE_ERROR (nm_setting_pppoe_error_get_type ()) 
+GType nm_setting_pppoe_error_get_type (void);
+
+#define NM_SETTING_PPPOE_ERROR nm_setting_pppoe_error_quark ()
+GQuark nm_setting_pppoe_error_quark (void);
+
+#define NM_SETTING_PPPOE_SERVICE  "service"
+#define NM_SETTING_PPPOE_USERNAME "username"
+#define NM_SETTING_PPPOE_PASSWORD "password"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingPPPOE;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingPPPOEClass;
+
+GType nm_setting_pppoe_get_type (void);
+
+NMSetting  *nm_setting_pppoe_new          (void);
+const char *nm_setting_pppoe_get_service  (NMSettingPPPOE *setting);
+const char *nm_setting_pppoe_get_username (NMSettingPPPOE *setting);
+const char *nm_setting_pppoe_get_password (NMSettingPPPOE *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_PPPOE_H */
diff --git a/libnm-util/nm-setting-serial.c b/libnm-util/nm-setting-serial.c
new file mode 100644
index 00000000..7a20688a
--- /dev/null
+++ b/libnm-util/nm-setting-serial.c
@@ -0,0 +1,314 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-setting-serial.h"
+#include "nm-setting-ppp.h"
+
+GQuark
+nm_setting_serial_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-serial-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_serial_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_SERIAL_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_SERIAL_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_SERIAL_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The required PPP setting is missing */
+			ENUM_ENTRY (NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING, "MissingPPPSetting"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingSerialError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING)
+
+#define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate))
+
+typedef struct {
+	guint baud;
+	guint bits;
+	char parity;
+	guint stopbits;
+	guint64 send_delay;
+} NMSettingSerialPrivate;
+
+
+enum {
+	PROP_0,
+	PROP_BAUD,
+	PROP_BITS,
+	PROP_PARITY,
+	PROP_STOPBITS,
+	PROP_SEND_DELAY,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_serial_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_SERIAL, NULL);
+}
+
+guint
+nm_setting_serial_get_baud (NMSettingSerial *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->baud;
+}
+
+guint
+nm_setting_serial_get_bits (NMSettingSerial *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->bits;
+}
+
+char
+nm_setting_serial_get_parity (NMSettingSerial *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->parity;
+}
+
+guint
+nm_setting_serial_get_stopbits (NMSettingSerial *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->stopbits;
+}
+
+guint64
+nm_setting_serial_get_send_delay (NMSettingSerial *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->send_delay;
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	/* Serial connections require a PPP setting */
+	if (all_settings && 
+	    !g_slist_find_custom (all_settings, NM_SETTING_PPP_SETTING_NAME, find_setting_by_name)) {
+		g_set_error (error,
+		             NM_SETTING_SERIAL_ERROR,
+		             NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING,
+		             NULL);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_serial_init (NMSettingSerial *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_SERIAL_SETTING_NAME, NULL);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_BAUD:
+		priv->baud = g_value_get_uint (value);
+		break;
+	case PROP_BITS:
+		priv->bits = g_value_get_uint (value);
+		break;
+	case PROP_PARITY:
+		priv->parity = g_value_get_char (value);
+		break;
+	case PROP_STOPBITS:
+		priv->stopbits = g_value_get_uint (value);
+		break;
+	case PROP_SEND_DELAY:
+		priv->send_delay = g_value_get_uint64 (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingSerial *setting = NM_SETTING_SERIAL (object);
+
+	switch (prop_id) {
+	case PROP_BAUD:
+		g_value_set_uint (value, nm_setting_serial_get_baud (setting));
+		break;
+	case PROP_BITS:
+		g_value_set_uint (value, nm_setting_serial_get_bits (setting));
+		break;
+	case PROP_PARITY:
+		g_value_set_char (value, nm_setting_serial_get_parity (setting));
+		break;
+	case PROP_STOPBITS:
+		g_value_set_uint (value, nm_setting_serial_get_stopbits (setting));
+		break;
+	case PROP_SEND_DELAY:
+		g_value_set_uint64 (value, nm_setting_serial_get_send_delay (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_serial_class_init (NMSettingSerialClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingSerialPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	parent_class->verify       = verify;
+
+	/* Properties */
+
+	/**
+	 * NMSettingSerial:baud:
+	 *
+	 * Speed to use for communication over the serial port.  Note that this value
+	 * usually has no effect for mobile broadband modems as they generally
+	 * ignore speed settings and use the highest available speed.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BAUD,
+		 g_param_spec_uint (NM_SETTING_SERIAL_BAUD,
+						"Baud",
+						"Speed to use for communication over the serial port.  "
+						"Note that this value usually has no effect for mobile "
+						"broadband modems as they generally ignore speed "
+						"settings and use the highest available speed.",
+						0, G_MAXUINT, 57600,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingSerial:bits:
+	 *
+	 * Byte-width of the serial communication.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BITS,
+		 g_param_spec_uint (NM_SETTING_SERIAL_BITS,
+						"Bits",
+						"Byte-width of the serial communication.  The 8 in "
+						"'8n1' for example.",
+						5, 8, 8,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingSerial:parity:
+	 *
+	 * Parity setting of the serial port.  Either 'E' for even parity, 'o' for
+	 * odd parity, or 'n' for no parity.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PARITY,
+		 g_param_spec_char (NM_SETTING_SERIAL_PARITY,
+						"Parity",
+						"Parity setting of the serial port.  Either 'E' for even "
+						"parity, 'o' for odd parity, or 'n' for no parity.",
+						'E', 'o', 'n',
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingSerial:stopbits:
+	 *
+	 * Number of stop bits for communication on the serial port.  Either 1 or 2.
+	 * The 1 in '8n1' for example.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_STOPBITS,
+		 g_param_spec_uint (NM_SETTING_SERIAL_STOPBITS,
+						"Stopbits",
+						"Number of stop bits for communication on the serial "
+						"port.  Either 1 or 2.  The 1 in '8n1' for example.",
+						1, 2, 1,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingSerial:send-delay:
+	 *
+	 * Time to delay between each byte sent to the modem, in microseconds.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SEND_DELAY,
+		 g_param_spec_uint64 (NM_SETTING_SERIAL_SEND_DELAY,
+						  "SendDelay",
+						  "Time to delay between each byte sent to the modem, "
+						  "in microseconds.",
+						  0, G_MAXUINT64, 0,
+						  G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+}
diff --git a/libnm-util/nm-setting-serial.h b/libnm-util/nm-setting-serial.h
new file mode 100644
index 00000000..d39e5a79
--- /dev/null
+++ b/libnm-util/nm-setting-serial.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_SERIAL_H
+#define NM_SETTING_SERIAL_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_SERIAL            (nm_setting_serial_get_type ())
+#define NM_SETTING_SERIAL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_SERIAL, NMSettingSerial))
+#define NM_SETTING_SERIAL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_SERIAL, NMSettingSerialClass))
+#define NM_IS_SETTING_SERIAL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_SERIAL))
+#define NM_IS_SETTING_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_SERIAL))
+#define NM_SETTING_SERIAL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_SERIAL, NMSettingSerialClass))
+
+#define NM_SETTING_SERIAL_SETTING_NAME "serial"
+
+typedef enum
+{
+	NM_SETTING_SERIAL_ERROR_UNKNOWN = 0,
+	NM_SETTING_SERIAL_ERROR_INVALID_PROPERTY,
+	NM_SETTING_SERIAL_ERROR_MISSING_PROPERTY,
+	NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING
+} NMSettingSerialError;
+
+#define NM_TYPE_SETTING_SERIAL_ERROR (nm_setting_serial_error_get_type ()) 
+GType nm_setting_serial_error_get_type (void);
+
+#define NM_SETTING_SERIAL_ERROR nm_setting_serial_error_quark ()
+GQuark nm_setting_serial_error_quark (void);
+
+#define NM_SETTING_SERIAL_BAUD "baud"
+#define NM_SETTING_SERIAL_BITS "bits"
+#define NM_SETTING_SERIAL_PARITY "parity"
+#define NM_SETTING_SERIAL_STOPBITS "stopbits"
+#define NM_SETTING_SERIAL_SEND_DELAY "send-delay"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingSerial;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingSerialClass;
+
+GType nm_setting_serial_get_type (void);
+
+NMSetting *nm_setting_serial_new            (void);
+guint      nm_setting_serial_get_baud       (NMSettingSerial *setting);
+guint      nm_setting_serial_get_bits       (NMSettingSerial *setting);
+char       nm_setting_serial_get_parity     (NMSettingSerial *setting);
+guint      nm_setting_serial_get_stopbits   (NMSettingSerial *setting);
+guint64    nm_setting_serial_get_send_delay (NMSettingSerial *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_SERIAL_H */
diff --git a/libnm-util/nm-setting-vpn.c b/libnm-util/nm-setting-vpn.c
new file mode 100644
index 00000000..53b609e2
--- /dev/null
+++ b/libnm-util/nm-setting-vpn.c
@@ -0,0 +1,464 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include "nm-setting-vpn.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+
+GQuark
+nm_setting_vpn_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-vpn-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_vpn_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_VPN_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_VPN_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_VPN_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingVpnError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingVPN, nm_setting_vpn, NM_TYPE_SETTING)
+
+#define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVPNPrivate))
+
+typedef struct {
+	char *service_type;
+
+	/* username of the user requesting this connection, thus
+	 * it's really only valid for user connections, and it also
+	 * should never be saved out to persistent config.
+	 */
+	char *user_name;
+
+	/* The hash table is created at setting object
+	 * init time and should not be replaced.  It is
+	 * a char * -> char * mapping, and both the key
+	 * and value are owned by the hash table, and should
+	 * be allocated with functions whose value can be
+	 * freed with g_free().  Should not contain secrets.
+	 */
+	GHashTable *data;
+
+	/* The hash table is created at setting object
+	 * init time and should not be replaced.  It is
+	 * a char * -> char * mapping, and both the key
+	 * and value are owned by the hash table, and should
+	 * be allocated with functions whose value can be
+	 * freed with g_free().  Should contain secrets only.
+	 */
+	GHashTable *secrets;
+} NMSettingVPNPrivate;
+
+enum {
+	PROP_0,
+	PROP_SERVICE_TYPE,
+	PROP_USER_NAME,
+	PROP_DATA,
+	PROP_SECRETS,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_vpn_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL);
+}
+
+const char *
+nm_setting_vpn_get_service_type (NMSettingVPN *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+	return NM_SETTING_VPN_GET_PRIVATE (setting)->service_type;
+}
+
+const char *
+nm_setting_vpn_get_user_name (NMSettingVPN *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+	return NM_SETTING_VPN_GET_PRIVATE (setting)->user_name;
+}
+
+void
+nm_setting_vpn_add_data_item (NMSettingVPN *setting,
+                              const char *key,
+                              const char *item)
+{
+	g_return_if_fail (NM_IS_SETTING_VPN (setting));
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (strlen (key) > 0);
+	g_return_if_fail (item != NULL);
+	g_return_if_fail (strlen (item) > 0);
+
+	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
+	                     g_strdup (key), g_strdup (item));
+}
+
+const char *
+nm_setting_vpn_get_data_item (NMSettingVPN *setting, const char *key)
+{
+	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+	return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
+}
+
+void
+nm_setting_vpn_remove_data_item (NMSettingVPN *setting, const char *key)
+{
+	g_return_if_fail (NM_IS_SETTING_VPN (setting));
+
+	g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
+}
+
+void
+nm_setting_vpn_foreach_data_item (NMSettingVPN *setting,
+                                  VPNIterFunc func,
+                                  gpointer user_data)
+{
+	g_return_if_fail (NM_IS_SETTING_VPN (setting));
+
+	g_hash_table_foreach (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
+	                      (GHFunc) func, user_data);
+}
+
+void
+nm_setting_vpn_add_secret (NMSettingVPN *setting,
+                           const char *key,
+                           const char *secret)
+{
+	g_return_if_fail (NM_IS_SETTING_VPN (setting));
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (strlen (key) > 0);
+	g_return_if_fail (secret != NULL);
+	g_return_if_fail (strlen (secret) > 0);
+
+	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
+	                     g_strdup (key), g_strdup (secret));
+}
+
+const char *
+nm_setting_vpn_get_secret (NMSettingVPN *setting, const char *key)
+{
+	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+	return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
+}
+
+void
+nm_setting_vpn_remove_secret (NMSettingVPN *setting, const char *key)
+{
+	g_return_if_fail (NM_IS_SETTING_VPN (setting));
+
+	g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
+}
+
+void
+nm_setting_vpn_foreach_secret (NMSettingVPN *setting,
+                               VPNIterFunc func,
+                               gpointer user_data)
+{
+	g_return_if_fail (NM_IS_SETTING_VPN (setting));
+
+	g_hash_table_foreach (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
+	                      (GHFunc) func, user_data);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+	if (!priv->service_type) {
+		g_set_error (error,
+		             NM_SETTING_VPN_ERROR,
+		             NM_SETTING_VPN_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_VPN_SERVICE_TYPE);
+		return FALSE;
+	}
+
+	if (!strlen (priv->service_type)) {
+		g_set_error (error,
+		             NM_SETTING_VPN_ERROR,
+		             NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_VPN_SERVICE_TYPE);
+		return FALSE;
+	}
+
+	/* default username can be NULL, but can't be zero-length */
+	if (priv->user_name && !strlen (priv->user_name)) {
+		g_set_error (error,
+		             NM_SETTING_VPN_ERROR,
+		             NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_VPN_USER_NAME);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
+{
+	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+	char *str;
+
+	g_return_val_if_fail (key != NULL, FALSE);
+	g_return_val_if_fail (value != NULL, FALSE);
+
+	if (!G_VALUE_HOLDS_STRING (value)) {
+		g_set_error (error, NM_SETTING_ERROR,
+		             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+		             "%s", key);
+		return FALSE;
+	}
+
+	str = g_value_dup_string (value);
+	if (!str || !strlen (str)) {
+		g_set_error (error, NM_SETTING_ERROR,
+		             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+		             "Secret %s was empty", key);
+		g_free (str);
+		return FALSE;
+	}
+
+	g_hash_table_insert (priv->secrets, g_strdup (key), str);
+	return TRUE;
+}
+
+static void
+destroy_one_secret (gpointer data)
+{
+	char *secret = (char *) data;
+
+	/* Don't leave the secret lying around in memory */
+	memset (secret, 0, strlen (secret));
+	g_free (secret);
+}
+
+static void
+nm_setting_vpn_init (NMSettingVPN *setting)
+{
+	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_VPN_SETTING_NAME, NULL);
+	priv->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	priv->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_one_secret);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
+
+	g_free (priv->service_type);
+	g_free (priv->user_name);
+	g_hash_table_destroy (priv->data);
+	g_hash_table_destroy (priv->secrets);
+
+	G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object);
+}
+
+static void
+copy_hash (gpointer key, gpointer value, gpointer user_data)
+{
+	g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
+	GHashTable *new_hash;
+
+	switch (prop_id) {
+	case PROP_SERVICE_TYPE:
+		g_free (priv->service_type);
+		priv->service_type = g_value_dup_string (value);
+		break;
+	case PROP_USER_NAME:
+		g_free (priv->user_name);
+		priv->user_name = g_value_dup_string (value);
+		break;
+	case PROP_DATA:
+		/* Must make a deep copy of the hash table here... */
+		g_hash_table_remove_all (priv->data);
+		new_hash = g_value_get_boxed (value);
+		if (new_hash)
+			g_hash_table_foreach (new_hash, copy_hash, priv->data);
+		break;
+	case PROP_SECRETS:
+		/* Must make a deep copy of the hash table here... */
+		g_hash_table_remove_all (priv->secrets);
+		new_hash = g_value_get_boxed (value);
+		if (new_hash)
+			g_hash_table_foreach (new_hash, copy_hash, priv->secrets);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingVPN *setting = NM_SETTING_VPN (object);
+	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_SERVICE_TYPE:
+		g_value_set_string (value, nm_setting_vpn_get_service_type (setting));
+		break;
+	case PROP_USER_NAME:
+		g_value_set_string (value, nm_setting_vpn_get_user_name (setting));
+		break;
+	case PROP_DATA:
+		g_value_set_boxed (value, priv->data);
+		break;
+	case PROP_SECRETS:
+		g_value_set_boxed (value, priv->secrets);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_vpn_class_init (NMSettingVPNClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingVPNPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+	parent_class->update_one_secret = update_one_secret;
+
+	/* Properties */
+	/**
+	 * NMSettingVPN:service-type:
+	 *
+	 * D-Bus service name of the VPN plugin that this setting uses to connect
+	 * to its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc
+ 	 * plugin.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SERVICE_TYPE,
+		 g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE,
+						  "Service type",
+						  "D-Bus service name of the VPN plugin that this "
+						  "setting uses to connect to its network.  i.e. "
+						  "org.freedesktop.NetworkManager.vpnc for the vpnc "
+						  "plugin.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettinVPN:user-name:
+	 *
+	 * User name of the currently logged in user for connections provided by the
+	 * user settings service.  This name is provided to the VPN plugin to use in
+	 * lieu of a custom username provided by that VPN plugins specific
+	 * configuration.  The VPN plugin itself decides which user name to use.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_USER_NAME,
+		 g_param_spec_string (NM_SETTING_VPN_USER_NAME,
+						  "User name",
+						  "User name of the currently logged in user for "
+						  "connections provided by the user settings service.  "
+						  "This name is provided to the VPN plugin to use in "
+						  "lieu of a custom username provided by that VPN "
+						  "plugins specific configuration.  The VPN plugin "
+						  "itself decides which user name to use.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingVPN:data:
+	 *
+	 * Dictionary of key/value pairs of VPN plugin specific data.  Both keys
+	 * and values must be strings.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DATA,
+		 _nm_param_spec_specialized (NM_SETTING_VPN_DATA,
+							   "Data",
+							   "Dictionary of key/value pairs of VPN plugin "
+							   "specific data.  Both keys and values must be "
+							   "strings.",
+							   DBUS_TYPE_G_MAP_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingVPN:secrets:
+	 *
+	 * Dictionary of key/value pairs of VPN plugin specific secrets like
+	 * passwords or private keys.  Both keys and values must be strings.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SECRETS,
+		 _nm_param_spec_specialized (NM_SETTING_VPN_SECRETS,
+							   "Secrets",
+							   "Dictionary of key/value pairs of VPN plugin "
+							   "specific secrets like passwords or private keys."
+							   "  Both keys and values must be strings.",
+							   DBUS_TYPE_G_MAP_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+}
+
diff --git a/libnm-util/nm-setting-vpn.h b/libnm-util/nm-setting-vpn.h
new file mode 100644
index 00000000..9c684bbf
--- /dev/null
+++ b/libnm-util/nm-setting-vpn.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_VPN_H
+#define NM_SETTING_VPN_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_VPN            (nm_setting_vpn_get_type ())
+#define NM_SETTING_VPN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_VPN, NMSettingVPN))
+#define NM_SETTING_VPN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_VPN, NMSettingVPNClass))
+#define NM_IS_SETTING_VPN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_VPN))
+#define NM_IS_SETTING_VPN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_VPN))
+#define NM_SETTING_VPN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_VPN, NMSettingVPNClass))
+
+#define NM_SETTING_VPN_SETTING_NAME "vpn"
+
+typedef enum
+{
+	NM_SETTING_VPN_ERROR_UNKNOWN = 0,
+	NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
+	NM_SETTING_VPN_ERROR_MISSING_PROPERTY,
+} NMSettingVpnError;
+
+#define NM_TYPE_SETTING_VPN_ERROR (nm_setting_vpn_error_get_type ()) 
+GType nm_setting_vpn_error_get_type (void);
+
+#define NM_SETTING_VPN_ERROR nm_setting_vpn_error_quark ()
+GQuark nm_setting_vpn_error_quark (void);
+
+#define NM_SETTING_VPN_SERVICE_TYPE "service-type"
+#define NM_SETTING_VPN_USER_NAME    "user-name"
+#define NM_SETTING_VPN_DATA         "data"
+#define NM_SETTING_VPN_SECRETS      "secrets"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingVPN;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingVPNClass;
+
+typedef void (*VPNIterFunc) (const char *key, const char *value, gpointer user_data);
+
+GType nm_setting_vpn_get_type (void);
+
+NMSetting        *nm_setting_vpn_new               (void);
+const char       *nm_setting_vpn_get_service_type  (NMSettingVPN *setting);
+const char       *nm_setting_vpn_get_user_name     (NMSettingVPN *setting);
+
+void              nm_setting_vpn_add_data_item     (NMSettingVPN *setting,
+                                                    const char *key,
+                                                    const char *item);
+const char *      nm_setting_vpn_get_data_item     (NMSettingVPN *setting,
+                                                    const char *key);
+void              nm_setting_vpn_remove_data_item  (NMSettingVPN *setting,
+                                                    const char *key);
+void              nm_setting_vpn_foreach_data_item (NMSettingVPN *setting,
+                                                    VPNIterFunc func,
+                                                    gpointer user_data);
+
+void              nm_setting_vpn_add_secret        (NMSettingVPN *setting,
+                                                    const char *key,
+                                                    const char *secret);
+const char *      nm_setting_vpn_get_secret        (NMSettingVPN *setting,
+                                                    const char *key);
+void              nm_setting_vpn_remove_secret     (NMSettingVPN *setting,
+                                                    const char *key);
+void              nm_setting_vpn_foreach_secret    (NMSettingVPN *setting,
+                                                    VPNIterFunc func,
+                                                    gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_VPN_H */
diff --git a/libnm-util/nm-setting-wired.c b/libnm-util/nm-setting-wired.c
new file mode 100644
index 00000000..2f252433
--- /dev/null
+++ b/libnm-util/nm-setting-wired.c
@@ -0,0 +1,384 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <net/ethernet.h>
+#include <dbus/dbus-glib.h>
+#include "nm-setting-wired.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+
+GQuark
+nm_setting_wired_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-wired-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_wired_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_WIRED_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_WIRED_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingWiredError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING)
+
+#define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate))
+
+typedef struct {
+	char *port;
+	guint32 speed;
+	char *duplex;
+	gboolean auto_negotiate;
+	GByteArray *mac_address;
+	guint32 mtu;
+} NMSettingWiredPrivate;
+
+enum {
+	PROP_0,
+	PROP_PORT,
+	PROP_SPEED,
+	PROP_DUPLEX,
+	PROP_AUTO_NEGOTIATE,
+	PROP_MAC_ADDRESS,
+	PROP_MTU,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_wired_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRED, NULL);
+}
+
+const char *
+nm_setting_wired_get_port (NMSettingWired *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->port;
+}
+
+guint32
+nm_setting_wired_get_speed (NMSettingWired *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
+
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->speed;
+}
+
+const char *
+nm_setting_wired_get_duplex (NMSettingWired *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->duplex;
+}
+
+gboolean
+nm_setting_wired_get_auto_negotiate (NMSettingWired *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->auto_negotiate;
+}
+
+const GByteArray *
+nm_setting_wired_get_mac_address (NMSettingWired *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address;
+}
+
+guint32
+nm_setting_wired_get_mtu (NMSettingWired *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
+
+	return NM_SETTING_WIRED_GET_PRIVATE (setting)->mtu;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+	const char *valid_ports[] = { "tp", "aui", "bnc", "mii", NULL };
+	const char *valid_duplex[] = { "half", "full", NULL };
+
+	if (priv->port && !_nm_utils_string_in_list (priv->port, valid_ports)) {
+		g_set_error (error,
+		             NM_SETTING_WIRED_ERROR,
+		             NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRED_PORT);
+		return FALSE;
+	}
+
+	if (priv->duplex && !_nm_utils_string_in_list (priv->duplex, valid_duplex)) {
+		g_set_error (error,
+		             NM_SETTING_WIRED_ERROR,
+		             NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRED_DUPLEX);
+		return FALSE;
+	}
+
+	if (priv->mac_address && priv->mac_address->len != ETH_ALEN) {
+		g_set_error (error,
+		             NM_SETTING_WIRED_ERROR,
+		             NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRED_MAC_ADDRESS);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_wired_init (NMSettingWired *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_WIRED_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
+
+	g_free (priv->port);
+	g_free (priv->duplex);
+
+	if (priv->mac_address)
+		g_byte_array_free (priv->mac_address, TRUE);
+
+	G_OBJECT_CLASS (nm_setting_wired_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PORT:
+		g_free (priv->port);
+		priv->port = g_value_dup_string (value);
+		break;
+	case PROP_SPEED:
+		priv->speed = g_value_get_uint (value);
+		break;
+	case PROP_DUPLEX:
+		g_free (priv->duplex);
+		priv->duplex = g_value_dup_string (value);
+		break;
+	case PROP_AUTO_NEGOTIATE:
+		priv->auto_negotiate = g_value_get_boolean (value);
+		break;
+	case PROP_MAC_ADDRESS:
+		if (priv->mac_address)
+			g_byte_array_free (priv->mac_address, TRUE);
+		priv->mac_address = g_value_dup_boxed (value);
+		break;
+	case PROP_MTU:
+		priv->mtu = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingWired *setting = NM_SETTING_WIRED (object);
+
+	switch (prop_id) {
+	case PROP_PORT:
+		g_value_set_string (value, nm_setting_wired_get_port (setting));
+		break;
+	case PROP_SPEED:
+		g_value_set_uint (value, nm_setting_wired_get_speed (setting));
+		break;
+	case PROP_DUPLEX:
+		g_value_set_string (value, nm_setting_wired_get_duplex (setting));
+		break;
+	case PROP_AUTO_NEGOTIATE:
+		g_value_set_boolean (value, nm_setting_wired_get_auto_negotiate (setting));
+		break;
+	case PROP_MAC_ADDRESS:
+		g_value_set_boxed (value, nm_setting_wired_get_mac_address (setting));
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_wired_get_mtu (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingWiredPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+	/**
+	 * NMSettingWired:port:
+	 *
+	 * Specific port type to use if multiple the device supports multiple
+	 * attachment methods.  One of 'tp' (Twisted Pair), 'aui' (Attachment Unit
+	 * Interface), 'bnc' (Thin Ethernet) or 'mii' (Media Independent Interface.
+	 * If the device supports only one port type, this setting is ignored.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PORT,
+		 g_param_spec_string (NM_SETTING_WIRED_PORT,
+						  "Port",
+						  "Specific port type to use if multiple the device "
+						  "supports multiple attachment methods.  One of "
+						  "'tp' (Twisted Pair), 'aui' (Attachment Unit Interface), "
+						  "'bnc' (Thin Ethernet) or 'mii' (Media Independent "
+						  "Interface.  If the device supports only one port "
+						  "type, this setting is ignored.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWired:speed:
+	 *
+	 * If non-zero, request that the device use only the specified speed. 
+	 * In Mbit/s, ie 100 == 100Mbit/s.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SPEED,
+		 g_param_spec_uint (NM_SETTING_WIRED_SPEED,
+						"Speed",
+						"If non-zero, request that the device use only the "
+						"specified speed.  In Mbit/s, ie 100 == 100Mbit/s.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWired:duplex:
+	 *
+	 * If specified, request that the device only use the specified duplex mode.
+	 * Either 'half' or 'full'.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_DUPLEX,
+		 g_param_spec_string (NM_SETTING_WIRED_DUPLEX,
+						  "Duplex",
+						  "If specified, request that the device only use the "
+						  "specified duplex mode.  Either 'half' or 'full'.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingEthernet:auto-negotiate:
+	 *
+	 * If TRUE, allow auto-negotiation of port speed and duplex mode.  If FALSE,
+	 * do not allow auto-negotiation, in which case the 'speed' and 'duplex'
+	 * properties should be set.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_AUTO_NEGOTIATE,
+		 g_param_spec_boolean (NM_SETTING_WIRED_AUTO_NEGOTIATE,
+						   "AutoNegotiate",
+						   "If TRUE, allow auto-negotiation of port speed and "
+						   "duplex mode.  If FALSE, do not allow auto-negotiation,"
+						   "in which case the 'speed' and 'duplex' properties "
+						   "should be set.",
+						   TRUE,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWired:mac-address:
+	 *
+	 * If specified, this connection will only apply to the ethernet device
+	 * whose MAC address matches. This property does not change the MAC address
+	 * of the device (known as MAC spoofing).
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MAC_ADDRESS,
+		 _nm_param_spec_specialized (NM_SETTING_WIRED_MAC_ADDRESS,
+							   "MAC Address",
+							   "If specified, this connection will only apply to "
+							   "the ethernet device whose MAC address matches.  "
+							   "This property does not change the MAC address "
+							   "of the device (known as MAC spoofing).",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWired:mtu:
+	 *
+	 * If non-zero, only transmit packets of the specified size or smaller,
+	 * breaking larger packets up into multiple Ethernet frames.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MTU,
+		 g_param_spec_uint (NM_SETTING_WIRED_MTU,
+						"MTU",
+						"If non-zero, only transmit packets of the specified "
+						"size or smaller, breaking larger packets up into "
+						"multiple Ethernet frames.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+}
+
diff --git a/libnm-util/nm-setting-wired.h b/libnm-util/nm-setting-wired.h
new file mode 100644
index 00000000..44127347
--- /dev/null
+++ b/libnm-util/nm-setting-wired.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIRED_H
+#define NM_SETTING_WIRED_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIRED            (nm_setting_wired_get_type ())
+#define NM_SETTING_WIRED(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRED, NMSettingWired))
+#define NM_SETTING_WIRED_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRED, NMSettingWiredClass))
+#define NM_IS_SETTING_WIRED(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRED))
+#define NM_IS_SETTING_WIRED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_WIRED))
+#define NM_SETTING_WIRED_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRED, NMSettingWiredClass))
+
+#define NM_SETTING_WIRED_SETTING_NAME "802-3-ethernet"
+
+typedef enum
+{
+	NM_SETTING_WIRED_ERROR_UNKNOWN = 0,
+	NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+	NM_SETTING_WIRED_ERROR_MISSING_PROPERTY
+} NMSettingWiredError;
+
+#define NM_TYPE_SETTING_WIRED_ERROR (nm_setting_wired_error_get_type ()) 
+GType nm_setting_wired_error_get_type (void);
+
+#define NM_SETTING_WIRED_ERROR nm_setting_wired_error_quark ()
+GQuark nm_setting_wired_error_quark (void);
+
+#define NM_SETTING_WIRED_PORT "port"
+#define NM_SETTING_WIRED_SPEED "speed"
+#define NM_SETTING_WIRED_DUPLEX "duplex"
+#define NM_SETTING_WIRED_AUTO_NEGOTIATE "auto-negotiate"
+#define NM_SETTING_WIRED_MAC_ADDRESS "mac-address"
+#define NM_SETTING_WIRED_MTU "mtu"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingWired;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingWiredClass;
+
+GType nm_setting_wired_get_type (void);
+
+NMSetting        *nm_setting_wired_new                (void);
+const char       *nm_setting_wired_get_port           (NMSettingWired *setting);
+guint32           nm_setting_wired_get_speed          (NMSettingWired *setting);
+const char       *nm_setting_wired_get_duplex         (NMSettingWired *setting);
+gboolean          nm_setting_wired_get_auto_negotiate (NMSettingWired *setting);
+const GByteArray *nm_setting_wired_get_mac_address    (NMSettingWired *setting);
+guint32           nm_setting_wired_get_mtu            (NMSettingWired *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIRED_H */
diff --git a/libnm-util/nm-setting-wireless-security.c b/libnm-util/nm-setting-wireless-security.c
new file mode 100644
index 00000000..678a6fad
--- /dev/null
+++ b/libnm-util/nm-setting-wireless-security.c
@@ -0,0 +1,1208 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <dbus/dbus-glib.h>
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-utils-private.h"
+
+GQuark
+nm_setting_wireless_security_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-wireless-security-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_wireless_security_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The required 802.1x setting is missing */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING, "Missing8021xSetting"),
+			/* The LEAP authentication algorithm requires use of 802.1x key management. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, "LEAPRequires8021x"),
+			/* The LEAP authentication algorithm requires a username. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, "LEAPRequiresUsername"),
+			/* Shared Key authentication can only be used with WEP encryption. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP, "SharedKeyRequiresWEP"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingWirelessSecurityError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING)
+
+#define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate))
+
+typedef struct {
+	char *key_mgmt;
+	guint32 wep_tx_keyidx;
+	char *auth_alg;
+	GSList *proto; /* GSList of strings */
+	GSList *pairwise; /* GSList of strings */
+	GSList *group; /* GSList of strings */
+	char *leap_username;
+	char *wep_key0;
+	char *wep_key1;
+	char *wep_key2;
+	char *wep_key3;
+	char *psk;
+	char *leap_password;
+	NMWepKeyType wep_key_type;
+} NMSettingWirelessSecurityPrivate;
+
+enum {
+	PROP_0,
+	PROP_KEY_MGMT,
+	PROP_WEP_TX_KEYIDX,
+	PROP_AUTH_ALG,
+	PROP_PROTO,
+	PROP_PAIRWISE,
+	PROP_GROUP,
+	PROP_LEAP_USERNAME,
+	PROP_WEP_KEY0,
+	PROP_WEP_KEY1,
+	PROP_WEP_KEY2,
+	PROP_WEP_KEY3,
+	PROP_PSK,
+	PROP_LEAP_PASSWORD,
+	PROP_WEP_KEY_TYPE,
+
+	LAST_PROP
+};
+
+NMSetting *
+nm_setting_wireless_security_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, NULL);
+}
+
+const char *
+nm_setting_wireless_security_get_key_mgmt (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->key_mgmt;
+}
+
+guint32
+nm_setting_wireless_security_get_num_protos (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+	return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->proto);
+}
+
+const char *
+nm_setting_wireless_security_get_proto (NMSettingWirelessSecurity *setting, guint32 i)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->proto), NULL);
+
+	return (const char *) g_slist_nth_data (priv->proto, i);
+}
+
+gboolean
+nm_setting_wireless_security_add_proto (NMSettingWirelessSecurity *setting, const char *proto)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+	g_return_val_if_fail (proto != NULL, FALSE);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	for (iter = priv->proto; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (proto, (char *) iter->data))
+			return FALSE;
+	}
+
+	priv->proto = g_slist_append (priv->proto, g_ascii_strdown (proto, -1));
+	return TRUE;
+}
+
+void
+nm_setting_wireless_security_remove_proto (NMSettingWirelessSecurity *setting, guint32 i)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->proto, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->proto = g_slist_delete_link (priv->proto, elt);
+}
+
+void
+nm_setting_wireless_security_clear_protos (NMSettingWirelessSecurity *setting)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	nm_utils_slist_free (priv->proto, g_free);
+	priv->proto = NULL;
+}
+
+guint32
+nm_setting_wireless_security_get_num_pairwise (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+	return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->pairwise);
+}
+
+const char *
+nm_setting_wireless_security_get_pairwise (NMSettingWirelessSecurity *setting, guint32 i)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->pairwise), NULL);
+
+	return (const char *) g_slist_nth_data (priv->pairwise, i);
+}
+
+gboolean
+nm_setting_wireless_security_add_pairwise (NMSettingWirelessSecurity *setting, const char *pairwise)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+	g_return_val_if_fail (pairwise != NULL, FALSE);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (pairwise, (char *) iter->data))
+			return FALSE;
+	}
+
+	priv->pairwise = g_slist_append (priv->pairwise, g_ascii_strdown (pairwise, -1));
+	return TRUE;
+}
+
+void
+nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting, guint32 i)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->pairwise, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->pairwise = g_slist_delete_link (priv->pairwise, elt);
+}
+
+void
+nm_setting_wireless_security_clear_pairwise (NMSettingWirelessSecurity *setting)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	nm_utils_slist_free (priv->pairwise, g_free);
+	priv->pairwise = NULL;
+}
+
+guint32
+nm_setting_wireless_security_get_num_groups (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+	return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->group);
+}
+
+const char *
+nm_setting_wireless_security_get_group (NMSettingWirelessSecurity *setting, guint32 i)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	g_return_val_if_fail (i <= g_slist_length (priv->group), NULL);
+
+	return (const char *) g_slist_nth_data (priv->group, i);
+}
+
+gboolean
+nm_setting_wireless_security_add_group (NMSettingWirelessSecurity *setting, const char *group)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+	GSList *iter;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+	g_return_val_if_fail (group != NULL, FALSE);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	for (iter = priv->group; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (group, (char *) iter->data))
+			return FALSE;
+	}
+
+	priv->group = g_slist_append (priv->group, g_ascii_strdown (group, -1));
+	return TRUE;
+}
+
+void
+nm_setting_wireless_security_remove_group (NMSettingWirelessSecurity *setting, guint32 i)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+	GSList *elt;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	elt = g_slist_nth (priv->group, i);
+	g_return_if_fail (elt != NULL);
+
+	g_free (elt->data);
+	priv->group = g_slist_delete_link (priv->group, elt);
+}
+
+void
+nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	nm_utils_slist_free (priv->group, g_free);
+	priv->group = NULL;
+}
+
+const char *
+nm_setting_wireless_security_get_psk (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->psk;
+}
+
+const char *
+nm_setting_wireless_security_get_leap_username (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_username;
+}
+
+const char *
+nm_setting_wireless_security_get_leap_password (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_password;
+}
+
+const char *
+nm_setting_wireless_security_get_wep_key (NMSettingWirelessSecurity *setting, guint32 idx)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+	g_return_val_if_fail (idx < 4, NULL);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	if (idx == 0)
+		return priv->wep_key0;
+	else if (idx == 1)
+		return priv->wep_key1;
+	else if (idx == 2)
+		return priv->wep_key2;
+	else if (idx == 3)
+		return priv->wep_key3;
+
+	g_assert_not_reached ();
+	return NULL;
+}
+
+void
+nm_setting_wireless_security_set_wep_key (NMSettingWirelessSecurity *setting, guint32 idx, const char *key)
+{
+	NMSettingWirelessSecurityPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+	g_return_if_fail (idx < 4);
+
+	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	switch (idx) {
+	case 0:
+		g_free (priv->wep_key0);
+		priv->wep_key0 = g_strdup (key);
+		break;
+	case 1:
+		g_free (priv->wep_key1);
+		priv->wep_key1 = g_strdup (key);
+		break;
+	case 2:
+		g_free (priv->wep_key2);
+		priv->wep_key2 = g_strdup (key);
+		break;
+	case 3:
+		g_free (priv->wep_key3);
+		priv->wep_key3 = g_strdup (key);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+guint32
+nm_setting_wireless_security_get_wep_tx_keyidx (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_tx_keyidx;
+}
+
+const char *
+nm_setting_wireless_security_get_auth_alg (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->auth_alg;
+}
+
+NMWepKeyType
+nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_key_type;
+}
+
+static gboolean
+verify_wep_key (const char *key, NMWepKeyType wep_type)
+{
+	int keylen, i;
+
+	if (!key)
+		return FALSE;
+
+	keylen = strlen (key);
+	if (wep_type == NM_WEP_KEY_TYPE_KEY || NM_WEP_KEY_TYPE_UNKNOWN) {
+		if (keylen == 10 || keylen == 26) {
+			/* Hex key */
+			for (i = 0; i < keylen; i++) {
+				if (!isxdigit (key[i]))
+					return FALSE;
+			}
+		} else if (keylen == 5 || keylen == 13) {
+			/* ASCII key */
+			for (i = 0; i < keylen; i++) {
+				if (!isascii (key[i]))
+					return FALSE;
+			}
+		} else
+			return FALSE;
+
+	} else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) {
+		if (!keylen || keylen > 64)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_wpa_psk (const char *psk)
+{
+	int psklen, i;
+
+	if (!psk)
+		return FALSE;
+
+	psklen = strlen (psk);
+	if (psklen < 8 || psklen > 64)
+		return FALSE;
+
+	if (psklen == 64) {
+		/* Hex PSK */
+		for (i = 0; i < psklen; i++) {
+			if (!isxdigit (psk[i]))
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+	NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting);
+	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+	GPtrArray *secrets;
+
+	secrets = g_ptr_array_sized_new (4);
+	if (!secrets) {
+		g_warning ("Not enough memory to create required secrets array.");
+		return NULL;
+	}
+
+	g_assert (priv->key_mgmt);
+
+	/* Static WEP */
+	if (strcmp (priv->key_mgmt, "none") == 0) {
+		if ((priv->wep_tx_keyidx == 0) && !verify_wep_key (priv->wep_key0, priv->wep_key_type)) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+			return secrets;
+		}
+		if ((priv->wep_tx_keyidx == 1) && !verify_wep_key (priv->wep_key1, priv->wep_key_type)) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
+			return secrets;
+		}
+		if ((priv->wep_tx_keyidx == 2) && !verify_wep_key (priv->wep_key2, priv->wep_key_type)) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
+			return secrets;
+		}
+		if ((priv->wep_tx_keyidx == 3) && !verify_wep_key (priv->wep_key3, priv->wep_key_type)) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
+			return secrets;
+		}
+		goto no_secrets;
+	}
+
+	/* WPA-PSK infrastructure and adhoc */
+	if (   (strcmp (priv->key_mgmt, "wpa-none") == 0)
+	    || (strcmp (priv->key_mgmt, "wpa-psk") == 0)) {
+		if (!verify_wpa_psk (priv->psk)) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_PSK);
+			return secrets;
+		}
+		goto no_secrets;
+	}
+
+	/* LEAP */
+	if (   priv->auth_alg
+	    && !strcmp (priv->auth_alg, "leap")
+	    && !strcmp (priv->key_mgmt, "ieee8021x")) {
+		if (!priv->leap_password || !strlen (priv->leap_password)) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD);
+			return secrets;
+		}
+		goto no_secrets;
+	}
+
+	if (   (strcmp (priv->key_mgmt, "ieee8021x") == 0)
+	    || (strcmp (priv->key_mgmt, "wpa-eap") == 0)) {
+		/* Let caller check the 802.1x setting for secrets */
+		goto no_secrets;
+	}
+
+	g_assert_not_reached ();
+	return secrets;
+
+no_secrets:
+	if (secrets)
+		g_ptr_array_free (secrets, TRUE);
+	return NULL;
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting);
+	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+	const char *valid_key_mgmt[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", NULL };
+	const char *valid_auth_algs[] = { "open", "shared", "leap", NULL };
+	const char *valid_protos[] = { "wpa", "rsn", NULL };
+	const char *valid_pairwise[] = { "wep40", "wep104", "tkip", "ccmp", NULL };
+	const char *valid_groups[] = { "wep40", "wep104", "tkip", "ccmp", NULL };
+
+	if (!priv->key_mgmt) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+		return FALSE;
+	}
+
+	if (!_nm_utils_string_in_list (priv->key_mgmt, valid_key_mgmt)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+		return FALSE;
+	}
+
+	if (priv->auth_alg && !strcmp (priv->auth_alg, "leap")) {
+		/* LEAP must use ieee8021x key management */
+		if (strcmp (priv->key_mgmt, "ieee8021x")) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X,
+			             NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+			return FALSE;
+		}
+		if (!priv->leap_username) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
+			             NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+			return FALSE;
+		}
+		if (priv->leap_password && !strlen (priv->leap_password)) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD);
+			return FALSE;
+		}
+	} else {
+		if (   (strcmp (priv->key_mgmt, "ieee8021x") == 0)
+		    || (strcmp (priv->key_mgmt, "wpa-eap") == 0)) {
+			/* Need an 802.1x setting too */
+			if (!g_slist_find_custom (all_settings, NM_SETTING_802_1X_SETTING_NAME, find_setting_by_name)) {
+				g_set_error (error,
+				             NM_SETTING_WIRELESS_SECURITY_ERROR,
+				             NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING,
+				             NULL);
+				return FALSE;
+			}
+		}
+	}
+
+	if (priv->leap_username && !strlen (priv->leap_username)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+		return FALSE;
+	}
+
+	if (priv->wep_tx_keyidx > 3) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX);
+		return FALSE;
+	}
+
+	if (priv->wep_key_type > NM_WEP_KEY_TYPE_LAST) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE);
+		return FALSE;
+	}
+
+	if (priv->wep_key0 && !verify_wep_key (priv->wep_key0, priv->wep_key_type)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+		return FALSE;
+	}
+	if (priv->wep_key1 && !verify_wep_key (priv->wep_key1, priv->wep_key_type)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
+		return FALSE;
+	}
+	if (priv->wep_key2 && !verify_wep_key (priv->wep_key2, priv->wep_key_type)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
+		return FALSE;
+	}
+	if (priv->wep_key3 && !verify_wep_key (priv->wep_key3, priv->wep_key_type)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
+		return FALSE;
+	}
+
+	if (priv->auth_alg && !_nm_utils_string_in_list (priv->auth_alg, valid_auth_algs)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+		return FALSE;
+	}
+
+	if (priv->psk && !verify_wpa_psk (priv->psk)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_PSK);
+		return FALSE;
+	}
+
+	if (priv->proto && !_nm_utils_string_slist_validate (priv->proto, valid_protos)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_PROTO);
+		return FALSE;
+	}
+
+	if (priv->pairwise) {
+		const char *wpa_none[] = { "wpa-none", NULL };
+
+		/* For ad-hoc connections, pairwise must be "none" */
+		if (_nm_utils_string_in_list (priv->key_mgmt, wpa_none)) {
+			GSList *iter;
+			gboolean found = FALSE;
+
+			for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) {
+				if (!strcmp ((char *) iter->data, "none")) {
+					found = TRUE;
+					break;
+				}
+			}
+
+			/* pairwise cipher list didn't contain "none", which is invalid
+			 * for WPA adhoc connections.
+			 */
+			if (!found) {
+				g_set_error (error,
+				             NM_SETTING_WIRELESS_SECURITY_ERROR,
+				             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+				return FALSE;
+			}
+		} else if (!_nm_utils_string_slist_validate (priv->pairwise, valid_pairwise)) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+			return FALSE;
+		}
+	}
+
+	if (priv->group && !_nm_utils_string_slist_validate (priv->group, valid_groups)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SECURITY_GROUP);
+		return FALSE;
+	}
+
+	/* Shared Key auth can only be used with WEP */
+	if (priv->auth_alg && !strcmp (priv->auth_alg, "shared")) {
+		if (priv->key_mgmt && strcmp (priv->key_mgmt, "none")) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP,
+			             NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_wireless_security_init (NMSettingWirelessSecurity *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (object);
+	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+
+	/* Strings first. g_free() already checks for NULLs so we don't have to */
+
+	g_free (priv->key_mgmt);
+	g_free (priv->auth_alg);
+	g_free (priv->leap_username);
+	g_free (priv->wep_key0);
+	g_free (priv->wep_key1);
+	g_free (priv->wep_key2);
+	g_free (priv->wep_key3);
+	g_free (priv->psk);
+	g_free (priv->leap_password);
+
+	nm_utils_slist_free (priv->proto, g_free);
+	nm_utils_slist_free (priv->pairwise, g_free);
+	nm_utils_slist_free (priv->group, g_free);
+
+	G_OBJECT_CLASS (nm_setting_wireless_security_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object);
+	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	const char *str;
+
+	switch (prop_id) {
+	case PROP_KEY_MGMT:
+		g_free (priv->key_mgmt);
+		str = g_value_get_string (value);
+		priv->key_mgmt = str ? g_ascii_strdown (str, -1) : NULL;
+		break;
+	case PROP_WEP_TX_KEYIDX:
+		priv->wep_tx_keyidx = g_value_get_uint (value);
+		break;
+	case PROP_AUTH_ALG:
+		g_free (priv->auth_alg);
+		str = g_value_get_string (value);
+		priv->auth_alg = str ? g_ascii_strdown (str, -1) : NULL;
+		break;
+	case PROP_PROTO:
+		nm_utils_slist_free (priv->proto, g_free);
+		priv->proto = g_value_dup_boxed (value);
+		break;
+	case PROP_PAIRWISE:
+		nm_utils_slist_free (priv->pairwise, g_free);
+		priv->pairwise = g_value_dup_boxed (value);
+		break;
+	case PROP_GROUP:
+		nm_utils_slist_free (priv->group, g_free);
+		priv->group = g_value_dup_boxed (value);
+		break;
+	case PROP_LEAP_USERNAME:
+		g_free (priv->leap_username);
+		priv->leap_username = g_value_dup_string (value);
+		break;
+	case PROP_WEP_KEY0:
+		nm_setting_wireless_security_set_wep_key (setting, 0, g_value_get_string (value));
+		break;
+	case PROP_WEP_KEY1:
+		nm_setting_wireless_security_set_wep_key (setting, 1, g_value_get_string (value));
+		break;
+	case PROP_WEP_KEY2:
+		nm_setting_wireless_security_set_wep_key (setting, 2, g_value_get_string (value));
+		break;
+	case PROP_WEP_KEY3:
+		nm_setting_wireless_security_set_wep_key (setting, 3, g_value_get_string (value));
+		break;
+	case PROP_PSK:
+		g_free (priv->psk);
+		priv->psk = g_value_dup_string (value);
+		break;
+	case PROP_LEAP_PASSWORD:
+		g_free (priv->leap_password);
+		priv->leap_password = g_value_dup_string (value);
+		break;
+	case PROP_WEP_KEY_TYPE:
+		priv->wep_key_type = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object);
+	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_KEY_MGMT:
+		g_value_set_string (value, priv->key_mgmt);
+		break;
+	case PROP_WEP_TX_KEYIDX:
+		g_value_set_uint (value, priv->wep_tx_keyidx);
+		break;
+	case PROP_AUTH_ALG:
+		g_value_set_string (value, priv->auth_alg);
+		break;
+	case PROP_PROTO:
+		g_value_set_boxed (value, priv->proto);
+		break;
+	case PROP_PAIRWISE:
+		g_value_set_boxed (value, priv->pairwise);
+		break;
+	case PROP_GROUP:
+		g_value_set_boxed (value, priv->group);
+		break;
+	case PROP_LEAP_USERNAME:
+		g_value_set_string (value, priv->leap_username);
+		break;
+	case PROP_WEP_KEY0:
+		g_value_set_string (value, priv->wep_key0);
+		break;
+	case PROP_WEP_KEY1:
+		g_value_set_string (value, priv->wep_key1);
+		break;
+	case PROP_WEP_KEY2:
+		g_value_set_string (value, priv->wep_key2);
+		break;
+	case PROP_WEP_KEY3:
+		g_value_set_string (value, priv->wep_key3);
+		break;
+	case PROP_PSK:
+		g_value_set_string (value, priv->psk);
+		break;
+	case PROP_LEAP_PASSWORD:
+		g_value_set_string (value, priv->leap_password);
+		break;
+	case PROP_WEP_KEY_TYPE:
+		g_value_set_uint (value, priv->wep_key_type);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingWirelessSecurityPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+
+	parent_class->verify         = verify;
+	parent_class->need_secrets   = need_secrets;
+
+	/* Properties */
+	/**
+	 * NMSettingWirelessSecurity:key-mgmt:
+	 *
+	 * Key management used for the connection.  One of 'none' (WEP), 'ieee8021x'
+	 * (Dynamic WEP), 'wpa-none' (Ad-Hoc WPA-PSK), 'wpa-psk' (infrastructure
+	 * WPA-PSK), or 'wpa-eap' (WPA-Enterprise).  This property must be set for
+	 * any WiFi connection that uses security.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_KEY_MGMT,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+						  "Key management",
+						  "Key management used for the connection.  One of "
+						  "'none' (WEP), 'ieee8021x' (Dynamic WEP), 'wpa-none' "
+						  "(WPA-PSK Ad-Hoc), 'wpa-psk' (infrastructure WPA-PSK), "
+						  "or 'wpa-eap' (WPA-Enterprise).  This property must "
+						  "be set for any WiFi connection that uses security.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_REQUIRED));
+
+	/**
+	 * NMSettingWirelessSecurity:wep-tx-keyidx:
+	 *
+	 * When static WEP is used (ie, key-mgmt = 'none') and a non-default WEP key
+	 * index is used by the AP, put that WEP key index here.  Valid values are 0
+	 * (default key) through 3.  Note that some consumer access points (like the
+	 * Linksys WRT54G) number the keys 1 - 4.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_WEP_TX_KEYIDX,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX,
+						"WEP TX key index",
+						"When static WEP is used (ie, key-mgmt = 'none') and a "
+						"non-default WEP key index is used by the AP, put that "
+						"WEP key index here.  Valid values are 0 (default key) "
+						"through 3.  Note that some consumer access points "
+						"(like the Linksys WRT54G) number the keys 1 - 4.",
+						0, 3, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWirelessSecurity:auth-alg:
+	 *
+	 * When WEP is used (ie, key-mgmt = 'none' or 'ieee8021x') indicate the
+	 * 802.11 authentication algorithm required by the AP here.  One of 'open'
+	 * for Open System, 'shared' for Shared Key, or 'leap' for Cisco LEAP.
+	 * When using Cisco LEAP (ie, key-mgmt = 'ieee8021x' and auth-alg = 'leap')
+	 * the 'leap-username' and 'leap-password' properties must be specified.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_AUTH_ALG,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
+						  "AuthAlg",
+						  "When WEP is used (ie, key-mgmt = 'none' or "
+						  "'ieee8021x') indicate the 802.11 authentication "
+						  "algorithm required by the AP here.  One of 'open' for "
+						  "Open System, 'shared' for Shared Key, or 'leap' for "
+						  "Cisco LEAP.  When using Cisco LEAP (ie, key-mgmt = "
+						  "'ieee8021x' and auth-alg = 'leap') the 'leap-username' "
+						  "and 'leap-password' properties must be specified.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWirelessSecurity:proto:
+	 *
+	 * List of strings specifying the allowed WPA protocol versions to use.
+	 * Each element may be one 'wpa' (allow WPA) or 'rsn' (allow WPA2/RSN).  If
+	 * not specified, both WPA and RSN connections are allowed.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PROTO,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_PROTO,
+							   "Proto",
+							   "List of strings specifying the allowed WPA "
+							   "protocol versions to use.  Each element may be "
+							   "one 'wpa' (allow WPA) or 'rsn' (allow "
+							   "WPA2/RSN).  If not specified, both WPA and RSN "
+							   "connections are allowed.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWirelessSecurity:pairwise:
+	 *
+	 * If specified, will only connect to WPA networks that provide the
+	 * specified pairwise encryption capabilities.  Each element may be one of
+	 * 'wep40', 'wep104', 'tkip', or 'ccmp'.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PAIRWISE,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
+							   "Pairwise",
+							   "If specified, will only connect to WPA networks "
+							   "that provide the specified pairwise encryption "
+							   "capabilities.  Each element may be one of 'wep40', "
+							   "'wep104', 'tkip', or 'ccmp'.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWirelessSecurity:group:
+	 *
+	 * If specified, will only connect to WPA networks that provide the
+	 * specified group/multicast encryption capabilities.  Each element may be
+	 * one of 'wep40', 'wep104', 'tkip', or 'ccmp'.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_GROUP,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_GROUP,
+							   "Group",
+							   "If specified, will only connect to WPA networks "
+							   "that provide the specified group/multicast "
+							   "encryption capabilities.  Each element may be "
+							   "one of 'wep40', 'wep104', 'tkip', or 'ccmp'.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWirelessSecurity:leap-username:
+	 *
+	 * The login username for legacy LEAP connections (ie, key-mgmt =
+	 * 'ieee8021x' and auth-alg = 'leap').
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_LEAP_USERNAME,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME,
+						  "LEAP Username",
+						  "The login username for legacy LEAP connections "
+						  "(ie, key-mgmt = 'ieee8021x' and auth-alg = 'leap').",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWirelessSecurity:wep-key0:
+	 *
+	 * Index 0 WEP key.  This is the WEP key used in most networks.  See the
+	 * 'wep-key-type' property for a description of how this key is interpreted.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_WEP_KEY0,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0,
+						  "WEP key0",
+						  "Index 0 WEP key.  This is the WEP key used in most "
+						  "networks.  See the 'wep-key-type' property for a "
+						  "description of how this key is interpreted.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingWirelessSecurity:wep-key1:
+	 *
+	 * Index 1 WEP key.  This WEP index is not used by most networks.  See the
+	 * 'wep-key-type' property for a description of how this key is interpreted.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_WEP_KEY1,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1,
+						  "WEP key1",
+						  "Index 1 WEP key.  This WEP index is not used by most "
+						  "networks.  See the 'wep-key-type' property for a "
+						  "description of how this key is interpreted.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingWirelessSecurity:wep-key2:
+	 *
+	 * Index 2 WEP key.  This WEP index is not used by most networks.  See the
+	 * 'wep-key-type' property for a description of how this key is interpreted.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_WEP_KEY2,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2,
+						  "WEP key2",
+						  "Index 2 WEP key.  This WEP index is not used by most "
+						  "networks.  See the 'wep-key-type' property for a "
+						  "description of how this key is interpreted.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingWirelessSecurity:wep-key3:
+	 *
+	 * Index 3 WEP key.  This WEP index is not used by most networks.  See the
+	 * 'wep-key-type' property for a description of how this key is interpreted.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_WEP_KEY3,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3,
+						  "WEP key3",
+						  "Index 3 WEP key.  This WEP index is not used by most "
+						  "networks.  See the 'wep-key-type' property for a "
+						  "description of how this key is interpreted.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingWirelessSecurity:psk:
+	 *
+	 * Pre-Shared-Key for WPA networks.  If the key is 64-characters long, it
+	 * must contain only hexadecimal characters and is interpreted as a
+	 * hexadecimal WPA key.  Otherwise, the key must be between 8 and 63 ASCII
+	 * characters (as specified in the 802.11i standard) and is interpreted as
+	 * a WPA passphrase, and is hashed to derive the actual WPA-PSK used when
+	 * connecting to the WiFi network.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_PSK,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_PSK,
+						  "PSK",
+						  "Pre-Shared-Key for WPA networks.  If the key is "
+						  "64-characters long, it must contain only hexadecimal "
+						  "characters and is interpreted as a hexadecimal WPA "
+						  "key.  Otherwise, the key must be between 8 and 63 "
+						  "ASCII characters (as specified in the 802.11i standard) "
+						  "and is interpreted as a WPA passphrase, and is hashed "
+						  "to derive the actual WPA-PSK used when connecting to "
+						  "the WiFi network.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingWirelessSecurity:leap-password:
+	 *
+	 * The login password for legacy LEAP connections (ie, key-mgmt =
+	 * 'ieee8021x' and auth-alg = 'leap').
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_LEAP_PASSWORD,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+						  "LEAP Password",
+						  "The login password for legacy LEAP connections "
+						  "(ie, key-mgmt = 'ieee8021x' and auth-alg = 'leap').",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_SECRET));
+
+	/**
+	 * NMSettingWirelessSecurity:wep-key-type:
+	 *
+	 * Controls the interpretation of WEP keys.  Allowed values are 1 (interpret
+	 * WEP keys as hexadecimal or ASCII keys) or 2 (interpret WEP keys as WEP
+	 * Passphrases).  If set to 1 and the keys are hexadecimal, they must be
+	 * either 10 or 26 characters in length.  If set to 1 and the keys are
+	 * ASCII keys, they must be either 5 or 13 characters in length.  If set to
+	 * 2, the passphrase is hashed using the de-facto MD5 method to derive the
+	 * actual WEP key.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_WEP_KEY_TYPE,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
+						"WEP Key Type",
+						"Controls the interpretation of WEP keys.  Allowed values "
+						"are 1 (interpret WEP keys as hexadecimal or ASCII keys) "
+						"or 2 (interpret WEP keys as WEP Passphrases).  If set to "
+						"1 and the keys are hexadecimal, they must be either 10 or "
+						"26 characters in length.  If set to 1 and the keys are "
+						"ASCII keys, they must be either 5 or 13 characters in "
+						"length.  If set to 2, the passphrase is hashed using "
+						" the de-facto MD5 method to derive the actual WEP key.",
+						NM_WEP_KEY_TYPE_UNKNOWN,
+						NM_WEP_KEY_TYPE_LAST,
+						NM_WEP_KEY_TYPE_UNKNOWN,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+}
diff --git a/libnm-util/nm-setting-wireless-security.h b/libnm-util/nm-setting-wireless-security.h
new file mode 100644
index 00000000..90d971b2
--- /dev/null
+++ b/libnm-util/nm-setting-wireless-security.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIRELESS_SECURITY_H
+#define NM_SETTING_WIRELESS_SECURITY_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIRELESS_SECURITY            (nm_setting_wireless_security_get_type ())
+#define NM_SETTING_WIRELESS_SECURITY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurity))
+#define NM_SETTING_WIRELESS_SECURITY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityClass))
+#define NM_IS_SETTING_WIRELESS_SECURITY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY))
+#define NM_IS_SETTING_WIRELESS_SECURITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY))
+#define NM_SETTING_WIRELESS_SECURITY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityClass))
+
+#define NM_SETTING_WIRELESS_SECURITY_SETTING_NAME "802-11-wireless-security"
+
+typedef enum
+{
+	NM_SETTING_WIRELESS_SECURITY_ERROR_UNKNOWN = 0,
+	NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+	NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY,
+	NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING,
+	NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X,
+	NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
+	NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP
+} NMSettingWirelessSecurityError;
+
+#define NM_TYPE_SETTING_WIRELESS_SECURITY_ERROR (nm_setting_wireless_security_error_get_type ()) 
+GType nm_setting_wireless_security_error_get_type (void);
+
+#define NM_SETTING_WIRELESS_SECURITY_ERROR nm_setting_wireless_security_error_quark ()
+GQuark nm_setting_wireless_security_error_quark (void);
+
+typedef enum {
+	NM_WEP_KEY_TYPE_UNKNOWN = 0,
+	NM_WEP_KEY_TYPE_KEY = 1,          /* Hex or ASCII */
+	NM_WEP_KEY_TYPE_PASSPHRASE = 2,   /* 104/128-bit Passphrase */
+
+	NM_WEP_KEY_TYPE_LAST = NM_WEP_KEY_TYPE_PASSPHRASE
+} NMWepKeyType;
+
+#define NM_SETTING_WIRELESS_SECURITY_KEY_MGMT "key-mgmt"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX "wep-tx-keyidx"
+#define NM_SETTING_WIRELESS_SECURITY_AUTH_ALG "auth-alg"
+#define NM_SETTING_WIRELESS_SECURITY_PROTO "proto"
+#define NM_SETTING_WIRELESS_SECURITY_PAIRWISE "pairwise"
+#define NM_SETTING_WIRELESS_SECURITY_GROUP "group"
+#define NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME "leap-username"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY0 "wep-key0"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY1 "wep-key1"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY2 "wep-key2"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY3 "wep-key3"
+#define NM_SETTING_WIRELESS_SECURITY_PSK "psk"
+#define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD "leap-password"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE "wep-key-type"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingWirelessSecurity;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingWirelessSecurityClass;
+
+GType nm_setting_wireless_security_get_type (void);
+
+NMSetting * nm_setting_wireless_security_new               (void);
+
+const char *nm_setting_wireless_security_get_key_mgmt      (NMSettingWirelessSecurity *setting);
+
+guint32     nm_setting_wireless_security_get_num_protos    (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_proto         (NMSettingWirelessSecurity *setting, guint32 i);
+gboolean    nm_setting_wireless_security_add_proto         (NMSettingWirelessSecurity *setting, const char *proto);
+void        nm_setting_wireless_security_remove_proto      (NMSettingWirelessSecurity *setting, guint32 i);
+void        nm_setting_wireless_security_clear_protos      (NMSettingWirelessSecurity *setting);
+
+guint32     nm_setting_wireless_security_get_num_pairwise  (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_pairwise      (NMSettingWirelessSecurity *setting, guint32 i);
+gboolean    nm_setting_wireless_security_add_pairwise      (NMSettingWirelessSecurity *setting, const char *pairwise);
+void        nm_setting_wireless_security_remove_pairwise   (NMSettingWirelessSecurity *setting, guint32 i);
+void        nm_setting_wireless_security_clear_pairwise    (NMSettingWirelessSecurity *setting);
+
+guint32     nm_setting_wireless_security_get_num_groups    (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_group         (NMSettingWirelessSecurity *setting, guint32 i);
+gboolean    nm_setting_wireless_security_add_group         (NMSettingWirelessSecurity *setting, const char *group);
+void        nm_setting_wireless_security_remove_group      (NMSettingWirelessSecurity *setting, guint32 i);
+void        nm_setting_wireless_security_clear_groups      (NMSettingWirelessSecurity *setting);
+
+const char *nm_setting_wireless_security_get_psk           (NMSettingWirelessSecurity *setting);
+
+const char *nm_setting_wireless_security_get_leap_username (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_leap_password (NMSettingWirelessSecurity *setting);
+
+const char *nm_setting_wireless_security_get_wep_key       (NMSettingWirelessSecurity *setting, guint32 idx);
+void        nm_setting_wireless_security_set_wep_key       (NMSettingWirelessSecurity *setting, guint32 idx, const char *key);
+guint32     nm_setting_wireless_security_get_wep_tx_keyidx (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_auth_alg      (NMSettingWirelessSecurity *setting);
+NMWepKeyType nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIRELESS_SECURITY_H */
diff --git a/libnm-util/nm-setting-wireless.c b/libnm-util/nm-setting-wireless.c
new file mode 100644
index 00000000..9213429c
--- /dev/null
+++ b/libnm-util/nm-setting-wireless.c
@@ -0,0 +1,909 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <netinet/ether.h>
+#include <dbus/dbus-glib.h>
+
+#include "wireless-helper.h"
+
+#include "NetworkManager.h"
+#include "nm-setting-wireless.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-utils-private.h"
+
+GQuark
+nm_setting_wireless_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-wireless-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_wireless_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			/* Unknown error. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_ERROR_UNKNOWN, "UnknownError"),
+			/* The specified property was invalid. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, "InvalidProperty"),
+			/* The specified property was missing and is required. */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY, "MissingProperty"),
+			/* The required security setting is missing */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING, "MissingSecuritySetting"),
+			/* The 'channel' property requires a valid 'band' */
+			ENUM_ENTRY (NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND, "ChannelRequiresBand"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingWirelessError", values);
+	}
+	return etype;
+}
+
+
+G_DEFINE_TYPE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING)
+
+#define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate))
+
+typedef struct {
+	GByteArray *ssid;
+	char *mode;
+	char *band;
+	guint32 channel;
+	GByteArray *bssid;
+	guint32 rate;
+	guint32 tx_power;
+	GByteArray *mac_address;
+	guint32 mtu;
+	GSList *seen_bssids;
+	char *security;
+} NMSettingWirelessPrivate;
+
+enum {
+	PROP_0,
+	PROP_SSID,
+	PROP_MODE,
+	PROP_BAND,
+	PROP_CHANNEL,
+	PROP_BSSID,
+	PROP_RATE,
+	PROP_TX_POWER,
+	PROP_MAC_ADDRESS,
+	PROP_MTU,
+	PROP_SEEN_BSSIDS,
+	PROP_SEC,
+
+	LAST_PROP
+};
+
+static gboolean
+match_cipher (const char *cipher,
+              const char *expected,
+              guint32 wpa_flags,
+              guint32 rsn_flags,
+              guint32 flag)
+{
+	if (strcmp (cipher, expected) != 0)
+		return FALSE;
+
+	if (!(wpa_flags & flag) && !(rsn_flags & flag))
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean
+nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless,
+								    NMSettingWirelessSecurity *s_wireless_sec,
+								    guint32 ap_flags,
+								    guint32 ap_wpa,
+								    guint32 ap_rsn,
+								    guint32 ap_mode)
+{
+	NMSettingWirelessPrivate *priv;
+	const char *key_mgmt = NULL, *cipher;
+	guint32 num, i;
+	gboolean found = FALSE;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (s_wireless), FALSE);
+
+	priv = NM_SETTING_WIRELESS_GET_PRIVATE (s_wireless);
+
+	if (!priv->security) {
+		if (   (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+		    || (ap_wpa != NM_802_11_AP_SEC_NONE)
+		    || (ap_rsn != NM_802_11_AP_SEC_NONE))
+			return FALSE;
+		return TRUE;
+	}
+
+	if (strcmp (priv->security, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) != 0)
+		return FALSE;
+
+	if (s_wireless_sec)
+		key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
+
+	if (s_wireless_sec == NULL || !key_mgmt)
+		return FALSE;
+
+	/* Static WEP */
+	if (!strcmp (key_mgmt, "none")) {
+		if (   !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+		    || (ap_wpa != NM_802_11_AP_SEC_NONE)
+		    || (ap_rsn != NM_802_11_AP_SEC_NONE))
+			return FALSE;
+		return TRUE;
+	}
+
+	/* Adhoc WPA */
+	if (!strcmp (key_mgmt, "wpa-none")) {
+		if (ap_mode != NM_802_11_MODE_ADHOC)
+			return FALSE;
+		// FIXME: validate ciphers if the BSSID actually puts WPA/RSN IE in
+		// it's beacon
+		return TRUE;
+	}
+
+	/* Stuff after this point requires infrastructure */
+	if (ap_mode != NM_802_11_MODE_INFRA)
+		return FALSE;
+
+	/* Dynamic WEP or LEAP */
+	if (!strcmp (key_mgmt, "ieee8021x")) {
+		if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY))
+			return FALSE;
+
+		/* If the AP is advertising a WPA IE, make sure it supports WEP ciphers */
+		if (ap_wpa != NM_802_11_AP_SEC_NONE) {
+			if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+				return FALSE;
+
+			/* quick check; can't use AP if it doesn't support at least one
+			 * WEP cipher in both pairwise and group suites.
+			 */
+			if (   !(ap_wpa & (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104))
+			    || !(ap_wpa & (NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104)))
+				return FALSE;
+
+			/* Match at least one pairwise cipher with AP's capability if the
+			 * wireless-security setting explicitly lists pairwise ciphers
+			 */
+			num = nm_setting_wireless_security_get_num_pairwise (s_wireless_sec);
+			for (i = 0, found = FALSE; i < num; i++) {
+				cipher = nm_setting_wireless_security_get_pairwise (s_wireless_sec, i);
+				if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_wpa, NM_802_11_AP_SEC_PAIR_WEP40)))
+					break;
+				if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_wpa, NM_802_11_AP_SEC_PAIR_WEP104)))
+					break;
+			}
+			if (!found && num)
+				return FALSE;
+
+			/* Match at least one group cipher with AP's capability if the
+			 * wireless-security setting explicitly lists group ciphers
+			 */
+			num = nm_setting_wireless_security_get_num_groups (s_wireless_sec);
+			for (i = 0, found = FALSE; i < num; i++) {
+				cipher = nm_setting_wireless_security_get_group (s_wireless_sec, i);
+				if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_wpa, NM_802_11_AP_SEC_GROUP_WEP40)))
+					break;
+				if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_wpa, NM_802_11_AP_SEC_GROUP_WEP104)))
+					break;
+			}
+			if (!found && num)
+				return FALSE;
+		}
+		return TRUE;
+	}
+
+	/* WPA[2]-PSK and WPA[2] Enterprise */
+	if (   !strcmp (key_mgmt, "wpa-psk")
+	    || !strcmp (key_mgmt, "wpa-eap")) {
+
+		if (!strcmp (key_mgmt, "wpa-psk")) {
+			if (   !(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+			    && !(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK))
+				return FALSE;
+		} else if (!strcmp (key_mgmt, "wpa-eap")) {
+			if (   !(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
+			    && !(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+				return FALSE;
+		}
+
+		// FIXME: should handle WPA and RSN separately here to ensure that
+		// if the Connection only uses WPA we don't match a cipher against
+		// the AP's RSN IE instead
+
+		/* Match at least one pairwise cipher with AP's capability if the
+		 * wireless-security setting explicitly lists pairwise ciphers
+		 */
+		num = nm_setting_wireless_security_get_num_pairwise (s_wireless_sec);
+		for (i = 0, found = FALSE; i < num; i++) {
+			cipher = nm_setting_wireless_security_get_pairwise (s_wireless_sec, i);
+			if ((found = match_cipher (cipher, "tkip", ap_wpa, ap_rsn, NM_802_11_AP_SEC_PAIR_TKIP)))
+				break;
+			if ((found = match_cipher (cipher, "ccmp", ap_wpa, ap_rsn, NM_802_11_AP_SEC_PAIR_CCMP)))
+				break;
+		}
+		if (!found && num)
+			return FALSE;
+
+		/* Match at least one group cipher with AP's capability if the
+		 * wireless-security setting explicitly lists group ciphers
+		 */
+		num = nm_setting_wireless_security_get_num_groups (s_wireless_sec);
+		for (i = 0, found = FALSE; i < num; i++) {
+			cipher = nm_setting_wireless_security_get_group (s_wireless_sec, i);
+
+			if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_WEP40)))
+				break;
+			if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_WEP104)))
+				break;
+			if ((found = match_cipher (cipher, "tkip", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_TKIP)))
+				break;
+			if ((found = match_cipher (cipher, "ccmp", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_CCMP)))
+				break;
+		}
+		if (!found && num)
+			return FALSE;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+NMSetting *
+nm_setting_wireless_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS, NULL);
+}
+
+const GByteArray *
+nm_setting_wireless_get_ssid (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->ssid;
+}
+
+const char *
+nm_setting_wireless_get_mode (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mode;
+}
+
+const char *
+nm_setting_wireless_get_band (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->band;
+}
+
+guint32
+nm_setting_wireless_get_channel (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->channel;
+}
+
+const GByteArray *
+nm_setting_wireless_get_bssid (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->bssid;
+}
+
+guint32
+nm_setting_wireless_get_rate (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->rate;
+}
+
+guint32
+nm_setting_wireless_get_tx_power (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->tx_power;
+}
+
+const GByteArray *
+nm_setting_wireless_get_mac_address (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address;
+}
+
+guint32
+nm_setting_wireless_get_mtu (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mtu;
+}
+
+const char *
+nm_setting_wireless_get_security (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->security;
+}
+
+gboolean
+nm_setting_wireless_add_seen_bssid (NMSettingWireless *setting,
+									const char *bssid)
+{
+	NMSettingWirelessPrivate *priv;
+	char *lower_bssid;
+	GSList *iter;
+	gboolean found = FALSE;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE);
+	g_return_val_if_fail (bssid != NULL, FALSE);
+
+	lower_bssid = g_ascii_strdown (bssid, -1);
+	if (!lower_bssid)
+		return FALSE;
+
+	priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+
+	for (iter = priv->seen_bssids; iter; iter = iter->next) {
+		if (!strcmp ((char *) iter->data, lower_bssid)) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (!found)
+		priv->seen_bssids = g_slist_prepend (priv->seen_bssids, lower_bssid);
+	else
+		g_free (lower_bssid);
+
+	return !found;
+}
+
+guint32
+nm_setting_wireless_get_num_seen_bssids (NMSettingWireless *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+	return g_slist_length (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids);
+}
+
+const char *
+nm_setting_wireless_get_seen_bssid (NMSettingWireless *setting,
+									guint32 i)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+	return (const char *) g_slist_nth_data (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids, i);
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+	NMSetting *setting = NM_SETTING (a);
+	const char *str = (const char *) b;
+
+	return strcmp (nm_setting_get_name (setting), str);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+	const char *valid_modes[] = { "infrastructure", "adhoc", NULL };
+	const char *valid_bands[] = { "a", "bg", NULL };
+	GSList *iter;
+
+	if (!priv->ssid) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY,
+		             NM_SETTING_WIRELESS_SSID);
+		return FALSE;
+	}
+
+	if (!priv->ssid->len || priv->ssid->len > 32) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_SSID);
+		return FALSE;
+	}
+
+	if (priv->mode && !_nm_utils_string_in_list (priv->mode, valid_modes)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_MODE);
+		return FALSE;
+	}
+
+	if (priv->band && !_nm_utils_string_in_list (priv->band, valid_bands)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_BAND);
+		return FALSE;
+	}
+
+	if (priv->channel && !priv->band) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND,
+		             NM_SETTING_WIRELESS_BAND);
+		return FALSE;
+	}
+
+	if (priv->channel) {
+		if (!strcmp (priv->band, "a")) {
+			int i;
+			int valid_channels[] = { 7, 8, 9, 11, 12, 16, 34, 36, 40, 44, 48,
+			                         52, 56, 60, 64, 100, 104, 108, 112, 116,
+			                         120, 124, 128, 132, 136, 140, 149, 153,
+			                         157, 161, 165, 183, 184, 185, 187, 188,
+			                         192, 196, 0 };
+
+			for (i = 0; valid_channels[i]; i++) {
+				if (priv->channel == valid_channels[i])
+					break;
+			}
+
+			if (valid_channels[i] == 0) {
+				g_set_error (error,
+				             NM_SETTING_WIRELESS_ERROR,
+				             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_WIRELESS_CHANNEL);
+				return FALSE;
+			}
+		} else if (!strcmp (priv->band, "bg") && priv->channel > 14) {
+				g_set_error (error,
+				             NM_SETTING_WIRELESS_ERROR,
+				             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+				             NM_SETTING_WIRELESS_CHANNEL);
+			return FALSE;
+		}
+	}
+
+	if (priv->bssid && priv->bssid->len != ETH_ALEN) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_BSSID);
+		return FALSE;
+	}
+
+	if (priv->mac_address && priv->mac_address->len != ETH_ALEN) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		             NM_SETTING_WIRELESS_MAC_ADDRESS);
+		return FALSE;
+	}
+
+	for (iter = priv->seen_bssids; iter; iter = iter->next) {
+		struct ether_addr addr;
+
+		if (!ether_aton_r (iter->data, &addr)) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_ERROR,
+			             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_WIRELESS_SEEN_BSSIDS);
+			return FALSE;
+		}
+	}
+
+	if (   priv->security
+	    && !g_slist_find_custom (all_settings, priv->security, find_setting_by_name)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_ERROR,
+		             NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING,
+		             NULL);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_setting_wireless_init (NMSettingWireless *setting)
+{
+	g_object_set (setting, NM_SETTING_NAME, NM_SETTING_WIRELESS_SETTING_NAME, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object);
+
+	g_free (priv->mode);
+	g_free (priv->band);
+	g_free (priv->security);
+
+	if (priv->ssid)
+		g_byte_array_free (priv->ssid, TRUE);
+	if (priv->bssid)
+		g_byte_array_free (priv->bssid, TRUE);
+	if (priv->mac_address)
+		g_byte_array_free (priv->mac_address, TRUE);
+
+	nm_utils_slist_free (priv->seen_bssids, g_free);
+
+	G_OBJECT_CLASS (nm_setting_wireless_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_SSID:
+		if (priv->ssid)
+			g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = g_value_dup_boxed (value);
+		break;
+	case PROP_MODE:
+		g_free (priv->mode);
+		priv->mode = g_value_dup_string (value);
+		break;
+	case PROP_BAND:
+		g_free (priv->band);
+		priv->band = g_value_dup_string (value);
+		break;
+	case PROP_CHANNEL:
+		priv->channel = g_value_get_uint (value);
+		break;
+	case PROP_BSSID:
+		if (priv->bssid)
+			g_byte_array_free (priv->bssid, TRUE);
+		priv->bssid = g_value_dup_boxed (value);
+		break;
+	case PROP_RATE:
+		priv->rate = g_value_get_uint (value);
+		break;
+	case PROP_TX_POWER:
+		priv->tx_power = g_value_get_uint (value);
+		break;
+	case PROP_MAC_ADDRESS:
+		if (priv->mac_address)
+			g_byte_array_free (priv->mac_address, TRUE);
+		priv->mac_address = g_value_dup_boxed (value);
+		break;
+	case PROP_MTU:
+		priv->mtu = g_value_get_uint (value);
+		break;
+	case PROP_SEEN_BSSIDS:
+		nm_utils_slist_free (priv->seen_bssids, g_free);
+		priv->seen_bssids = g_value_dup_boxed (value);
+		break;
+	case PROP_SEC:
+		g_free (priv->security);
+		priv->security = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSettingWireless *setting = NM_SETTING_WIRELESS (object);
+
+	switch (prop_id) {
+	case PROP_SSID:
+		g_value_set_boxed (value, nm_setting_wireless_get_ssid (setting));
+		break;
+	case PROP_MODE:
+		g_value_set_string (value, nm_setting_wireless_get_mode (setting));
+		break;
+	case PROP_BAND:
+		g_value_set_string (value, nm_setting_wireless_get_band (setting));
+		break;
+	case PROP_CHANNEL:
+		g_value_set_uint (value, nm_setting_wireless_get_channel (setting));
+		break;
+	case PROP_BSSID:
+		g_value_set_boxed (value, nm_setting_wireless_get_bssid (setting));
+		break;
+	case PROP_RATE:
+		g_value_set_uint (value, nm_setting_wireless_get_rate (setting));
+		break;
+	case PROP_TX_POWER:
+		g_value_set_uint (value, nm_setting_wireless_get_tx_power (setting));
+		break;
+	case PROP_MAC_ADDRESS:
+		g_value_set_boxed (value, nm_setting_wireless_get_mac_address (setting));
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_wireless_get_mtu (setting));
+		break;
+	case PROP_SEEN_BSSIDS:
+		g_value_set_boxed (value, NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids);
+		break;
+	case PROP_SEC:
+		g_value_set_string (value, nm_setting_wireless_get_security (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingWirelessPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+	parent_class->verify       = verify;
+
+	/* Properties */
+	/**
+	 * NMSettingWireless:ssid:
+	 *
+	 * SSID of the WiFi network.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SSID,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_SSID,
+							   "SSID",
+							   "SSID of the WiFi network.  Must be specified.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWireless:mode:
+	 *
+	 * WiFi network mode; one of 'infrastructure' or 'adhoc'.  If blank,
+	 * infrastructure is assumed.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MODE,
+		 g_param_spec_string (NM_SETTING_WIRELESS_MODE,
+						  "Mode",
+						  "WiFi network mode; one of 'infrastructure' or "
+						  "'adhoc'.  If blank, infrastructure is assumed.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWireless:band:
+	 *
+	 * 802.11 frequency band of the network.  One of 'a' for 5GHz 802.11a or
+	 * 'bg' for 2.4GHz 802.11.  This will lock associations to the WiFi network
+	 * to the specific band, i.e. if 'a' is specified, the device will not
+	 * associate with the same network in the 2.4GHz band even if the network's
+	 * settings are compatible.  This setting depends on specific driver
+	 * capability and may not work with all drivers.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BAND,
+		 g_param_spec_string (NM_SETTING_WIRELESS_BAND,
+						  "Band",
+						  "802.11 frequency band of the network.  One of 'a' "
+						  "for 5GHz 802.11a or 'bg' for 2.4GHz 802.11.  This "
+						  "will lock associations to the WiFi network to the "
+						  "specific band, i.e. if 'a' is specified, the device "
+						  "will not associate with the same network in the "
+						  "2.4GHz band even if the network's settings are "
+						  "compatible.  This setting depends on specific driver "
+						  "capability and may not work with all drivers.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWireless:channel:
+	 *
+	 * Wireless channel to use for the WiFi connection.  The device will only
+	 * join (or create for Ad-Hoc networks) a WiFi network on the specified
+	 * channel.  Because channel numbers overlap between bands, this property
+	 * also requires the 'band' property to be set.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_CHANNEL,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_CHANNEL,
+						"Channel",
+						"Wireless channel to use for the WiFi connection.  The "
+						"device will only join (or create for Ad-Hoc networks) "
+						"a WiFi network on the specified channel.  Because "
+						"channel numbers overlap between bands, this property "
+						"also requires the 'band' property to be set.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWireless:bssid:
+	 *
+	 * If specified, directs the device to only associate with the given access
+	 * point.  This capability is highly driver dependent and not supported by
+	 * all devices.  Note: this property does not control the BSSID used when
+	 * creating an Ad-Hoc network and is unlikely to in the future.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_BSSID,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_BSSID,
+							   "BSSID",
+							   "If specified, directs the device to only associate "
+							   "with the given access point.  This capability is "
+							   "highly driver dependent and not supported by all "
+							   "devices.  Note: this property does not control "
+							   "the BSSID used when creating an Ad-Hoc network "
+							   "and is unlikely to in the future.",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWireless:rate:
+	 *
+	 * If non-zero, directs the device to only use the specified bitrate for
+	 * communication with the access point.  Units are in Kb/s, ie 5500 = 5.5
+	 * Mbit/s.  This property is highly driver dependent and not all devices
+	 * support setting a static bitrate.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_RATE,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_RATE,
+						"Rate",
+						"If non-zero, directs the device to only use the "
+						"specified bitrate for communication with the access "
+						"point.  Units are in Kb/s, ie 5500 = 5.5 Mbit/s.  This "
+						"property is highly driver dependent and not all devices "
+						"support setting a static bitrate.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingWireless:tx-power:
+	 *
+	 * If non-zero, directs the device to use the specified transmit power.
+	 * Units are dBm.  This property is highly driver dependent and not all
+	 * devices support setting a static transmit power.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_TX_POWER,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_TX_POWER,
+						"TX Power",
+						"If non-zero, directs the device to use the specified "
+						"transmit power.  Units are dBm.  This property is highly "
+						"driver dependent and not all devices support setting a "
+						"static transmit power.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingWireless:mac-address:
+	 *
+	 * If specified, this connection will only apply to the WiFi device
+	 * whose MAC address matches. This property does not change the MAC address
+	 * of the device (known as MAC spoofing).
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MAC_ADDRESS,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_MAC_ADDRESS,
+							   "MAC Address",
+							   "If specified, this connection will only apply to "
+							   "the WiFi device whose MAC address matches.  "
+							   "This property does not change the MAC address "
+							   "of the device (known as MAC spoofing).",
+							   DBUS_TYPE_G_UCHAR_ARRAY,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+
+	/**
+	 * NMSettingWireless:seen-bssids:
+	 *
+	 * A list of BSSIDs (each BSSID formatted as a MAC address like
+	 * '00:11:22:33:44:55') that have been detected as part of the WiFI network.
+	 * The settings service will usually populate this property by periodically
+	 * asking NetworkManager what the device's current AP is while connected
+	 * to the network (or monitoring the device's 'active-ap' property) and
+	 * adding the current AP'sBSSID to this list.  This list helps NetworkManager
+	 * find hidden APs by matching up scan results with the BSSIDs in this list.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SEEN_BSSIDS,
+		 _nm_param_spec_specialized (NM_SETTING_WIRELESS_SEEN_BSSIDS,
+							   "Seen BSSIDS",
+							   "A list of BSSIDs (each BSSID formatted as a MAC "
+							   "address like '00:11:22:33:44:55') that have been "
+							   "detected as part of the WiFI network.  The "
+							   "settings service will usually populate this "
+							   "property by periodically asking NetworkManager "
+							   "what the device's current AP is while connected "
+							   "to the network (or monitoring the device's "
+							   "'active-ap' property) and adding the current AP's "
+							   "BSSID to this list.  This list helps NetworkManager "
+							   "find hidden APs by matching up scan results with "
+							   "the BSSIDs in this list.",
+							   DBUS_TYPE_G_LIST_OF_STRING,
+							   G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingWireless:mtu:
+	 *
+	 * If non-zero, only transmit packets of the specified size or smaller,
+	 * breaking larger packets up into multiple Ethernet frames.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_MTU,
+		 g_param_spec_uint (NM_SETTING_WIRELESS_MTU,
+						"MTU",
+						"If non-zero, only transmit packets of the specified "
+						"size or smaller, breaking larger packets up into "
+						"multiple Ethernet frames.",
+						0, G_MAXUINT32, 0,
+						G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE));
+
+	/**
+	 * NMSettingWireless:security:
+	 *
+	 * If the wireless connection has any security restrictions, like 802.1x,
+	 * WEP, or WPA, set this property to '802-11-wireless-security' and ensure
+	 * the connection contains a valid 802-11-wireless-security setting.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_SEC,
+		 g_param_spec_string (NM_SETTING_WIRELESS_SEC,
+						  "Security",
+						  "If the wireless connection has any security "
+						  "restrictions, like 802.1x, WEP, or WPA, set this "
+						  "property to '" NM_SETTING_WIRELESS_SECURITY_SETTING_NAME "' "
+						  "and ensure the connection contains a valid "
+						  NM_SETTING_WIRELESS_SECURITY_SETTING_NAME " setting.",
+						  NULL,
+						  G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE));
+}
diff --git a/libnm-util/nm-setting-wireless.h b/libnm-util/nm-setting-wireless.h
new file mode 100644
index 00000000..da2ea5b6
--- /dev/null
+++ b/libnm-util/nm-setting-wireless.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIRELESS_H
+#define NM_SETTING_WIRELESS_H
+
+#include <nm-setting.h>
+#include <nm-setting-wireless-security.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIRELESS            (nm_setting_wireless_get_type ())
+#define NM_SETTING_WIRELESS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRELESS, NMSettingWireless))
+#define NM_SETTING_WIRELESS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessClass))
+#define NM_IS_SETTING_WIRELESS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRELESS))
+#define NM_IS_SETTING_WIRELESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_WIRELESS))
+#define NM_SETTING_WIRELESS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessClass))
+
+#define NM_SETTING_WIRELESS_SETTING_NAME "802-11-wireless"
+
+typedef enum
+{
+	NM_SETTING_WIRELESS_ERROR_UNKNOWN = 0,
+	NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+	NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY,
+	NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING,
+	NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND
+} NMSettingWirelessError;
+
+#define NM_TYPE_SETTING_WIRELESS_ERROR (nm_setting_wireless_error_get_type ()) 
+GType nm_setting_wireless_error_get_type (void);
+
+#define NM_SETTING_WIRELESS_ERROR nm_setting_wireless_error_quark ()
+GQuark nm_setting_wireless_error_quark (void);
+
+#define NM_SETTING_WIRELESS_SSID        "ssid"
+#define NM_SETTING_WIRELESS_MODE        "mode"
+#define NM_SETTING_WIRELESS_BAND        "band"
+#define NM_SETTING_WIRELESS_CHANNEL     "channel"
+#define NM_SETTING_WIRELESS_BSSID       "bssid"
+#define NM_SETTING_WIRELESS_RATE        "rate"
+#define NM_SETTING_WIRELESS_TX_POWER    "tx-power"
+#define NM_SETTING_WIRELESS_MAC_ADDRESS "mac-address"
+#define NM_SETTING_WIRELESS_MTU         "mtu"
+#define NM_SETTING_WIRELESS_SEEN_BSSIDS "seen-bssids"
+#define NM_SETTING_WIRELESS_SEC         "security"
+
+typedef struct {
+	NMSetting parent;
+} NMSettingWireless;
+
+typedef struct {
+	NMSettingClass parent;
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingWirelessClass;
+
+GType nm_setting_wireless_get_type (void);
+
+NMSetting *nm_setting_wireless_new (void);
+
+const GByteArray *nm_setting_wireless_get_ssid               (NMSettingWireless *setting);
+const char       *nm_setting_wireless_get_mode               (NMSettingWireless *setting);
+const char       *nm_setting_wireless_get_band               (NMSettingWireless *setting);
+guint32           nm_setting_wireless_get_channel            (NMSettingWireless *setting);
+const GByteArray *nm_setting_wireless_get_bssid              (NMSettingWireless *setting);
+guint32           nm_setting_wireless_get_rate               (NMSettingWireless *setting);
+guint32           nm_setting_wireless_get_tx_power           (NMSettingWireless *setting);
+const GByteArray *nm_setting_wireless_get_mac_address        (NMSettingWireless *setting);
+guint32           nm_setting_wireless_get_mtu                (NMSettingWireless *setting);
+const char       *nm_setting_wireless_get_security           (NMSettingWireless *setting);
+
+gboolean          nm_setting_wireless_add_seen_bssid         (NMSettingWireless *setting,
+															  const char *bssid);
+
+guint32           nm_setting_wireless_get_num_seen_bssids    (NMSettingWireless *setting);
+const char       *nm_setting_wireless_get_seen_bssid         (NMSettingWireless *setting,
+															  guint32 i);
+
+gboolean          nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless,
+															  NMSettingWirelessSecurity *s_wireless_sec,
+															  guint32 ap_flags,
+															  guint32 ap_wpa,
+															  guint32 ap_rsn,
+															  guint32 ap_mode);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIRELESS_H */
diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c
new file mode 100644
index 00000000..06994c73
--- /dev/null
+++ b/libnm-util/nm-setting.c
@@ -0,0 +1,752 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-setting.h"
+#include "nm-setting-connection.h"
+#include "nm-utils.h"
+
+/**
+ * SECTION:nm-setting
+ * @short_description: Describes related configuration information
+ * @include: nm-setting.h
+ *
+ * Each #NMSetting contains properties that describe configuration that applies
+ * to a specific network layer (like IPv4 or IPv6 configuration) or device type
+ * (like Ethernet, or WiFi).  A collection of individual settings together
+ * make up an #NMConnection. Each property is strongly typed and usually has
+ * a number of allowed values.  See each #NMSetting subclass for a description
+ * of properties and allowed values.
+ */
+
+/**
+ * nm_setting_error_quark:
+ *
+ * Registers an error quark for #NMSetting if necessary.
+ *
+ * Returns: the error quark used for NMSetting errors.
+ **/
+GQuark
+nm_setting_error_quark (void)
+{
+	static GQuark quark;
+
+	if (G_UNLIKELY (!quark))
+		quark = g_quark_from_static_string ("nm-setting-error-quark");
+	return quark;
+}
+
+/* This should really be standard. */
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_setting_error_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (NM_SETTING_ERROR_UNKNOWN, "UnknownError"),
+			ENUM_ENTRY (NM_SETTING_ERROR_PROPERTY_NOT_FOUND, "PropertyNotFound"),
+			ENUM_ENTRY (NM_SETTING_ERROR_PROPERTY_NOT_SECRET, "PropertyNotSecret"),
+			ENUM_ENTRY (NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, "PropertyTypeMismatch"),
+			{ 0, 0, 0 }
+		};
+		etype = g_enum_register_static ("NMSettingError", values);
+	}
+	return etype;
+}
+
+G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
+
+#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
+
+typedef struct {
+	char *name;
+} NMSettingPrivate;
+
+enum {
+	PROP_0,
+	PROP_NAME,
+
+	PROP_LAST
+};
+
+static void
+destroy_gvalue (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+/**
+ * nm_setting_to_hash:
+ * @setting: the #NMSetting
+ *
+ * Converts the #NMSetting into a #GHashTable mapping each setting property
+ * name to a GValue describing that property, suitable for marshalling over
+ * D-Bus or serializing.  The mapping is string:GValue.
+ * 
+ * Returns: a new #GHashTable describing the setting's properties
+ **/
+GHashTable *
+nm_setting_to_hash (NMSetting *setting)
+{
+	GHashTable *hash;
+	GParamSpec **property_specs;
+	guint n_property_specs;
+	guint i;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+	if (!property_specs) {
+		g_warning ("%s: couldn't find property specs for object of type '%s'",
+		           __func__, g_type_name (G_OBJECT_TYPE (setting)));
+		return NULL;
+	}
+
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+							(GDestroyNotify) g_free,
+							destroy_gvalue);
+
+	for (i = 0; i < n_property_specs; i++) {
+		GParamSpec *prop_spec = property_specs[i];
+
+		if (prop_spec->flags & NM_SETTING_PARAM_SERIALIZE) {
+			GValue *value;
+
+			value = g_slice_new0 (GValue);
+			g_value_init (value, prop_spec->value_type);
+			g_object_get_property (G_OBJECT (setting), prop_spec->name, value);
+
+			/* Don't serialize values with default values */
+			if (!g_param_value_defaults (prop_spec, value))
+				g_hash_table_insert (hash, g_strdup (prop_spec->name), value);
+			else
+				destroy_gvalue (value);
+		}
+	}
+
+	g_free (property_specs);
+
+	return hash;
+}
+
+typedef struct {
+	GObjectClass *class;
+	guint n_params;
+	GParameter *params;
+} NMSettingFromHashInfo;
+
+static void
+one_property_cb (gpointer key, gpointer val, gpointer user_data)
+{
+	const char *prop_name = (char *) key;
+	GValue *src_value = (GValue *) val;
+	NMSettingFromHashInfo *info = (NMSettingFromHashInfo *) user_data;
+	GValue *dst_value = &info->params[info->n_params].value;
+	GParamSpec *param_spec;
+
+	param_spec = g_object_class_find_property (info->class, prop_name);
+	if (!param_spec || !(param_spec->flags & NM_SETTING_PARAM_SERIALIZE)) {
+		/* Oh, we're so nice and only warn, maybe it should be a fatal error? */
+		nm_warning ("Ignoring invalid property '%s'", prop_name);
+		return;
+	}
+
+	g_value_init (dst_value, G_VALUE_TYPE (src_value));
+	if (g_value_transform (src_value, dst_value)) {
+		info->params[info->n_params].name = prop_name;
+		info->n_params++;
+	} else {
+		nm_warning ("Ignoring property '%s' with invalid type (%s)",
+				  prop_name, G_VALUE_TYPE_NAME (src_value));
+		g_value_unset (dst_value);
+	}
+}
+
+/**
+ * nm_setting_new_from_hash:
+ * @setting_type: the #NMSetting type which the hash contains properties for
+ * @hash: the #GHashTable containing a string:GValue mapping of properties
+ * that apply to the setting
+ *
+ * Creates a new #NMSetting object and populates that object with the properties
+ * contained in the hash table, using each hash key as the property to set,
+ * and each hash value as the value to set that property to.  Setting properties
+ * are strongly typed, thus the GValue type of the hash value must be correct.
+ * See the documentation on each #NMSetting object subclass for the correct
+ * property names and value types.
+ * 
+ * Returns: a new #NMSetting object populated with the properties from the
+ * hash table, or NULL on failure
+ **/
+NMSetting *
+nm_setting_new_from_hash (GType setting_type,
+                          GHashTable *hash)
+{
+	NMSetting *setting;
+	NMSettingFromHashInfo info;
+	int i;
+
+	g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
+	g_return_val_if_fail (hash != NULL, NULL);
+
+	info.class = g_type_class_ref (setting_type);
+	info.n_params = 0;
+	info.params = g_new0 (GParameter, g_hash_table_size (hash));
+
+	g_hash_table_foreach (hash, one_property_cb, &info);
+
+	setting = (NMSetting *) g_object_newv (setting_type, info.n_params, info.params);
+
+	for (i = 0; i < info.n_params; i++) {
+		GValue *v = &info.params[i].value;
+		g_value_unset (v);
+	}
+
+	g_free (info.params);
+	g_type_class_unref (info.class);
+
+	return setting;
+}
+
+static void
+duplicate_setting (NMSetting *setting,
+                   const char *name,
+                   const GValue *value,
+                   GParamFlags flags,
+                   gpointer user_data)
+{
+	if (flags & G_PARAM_WRITABLE)
+		g_object_set_property (G_OBJECT (user_data), name, value);
+}
+
+/**
+ * nm_setting_duplicate:
+ * @setting: the #NMSetting to duplicate
+ *
+ * Duplicates a #NMSetting.
+ *
+ * Returns: a new #NMSetting containing the same properties and values as the
+ * source #NMSetting
+ **/
+NMSetting *
+nm_setting_duplicate (NMSetting *setting)
+{
+	GObject *dup;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	dup = g_object_new (G_OBJECT_TYPE (setting), NULL);
+
+	g_object_freeze_notify (dup);
+	nm_setting_enumerate_values (setting, duplicate_setting, dup);
+	g_object_thaw_notify (dup);
+
+	return NM_SETTING (dup);
+}
+
+/**
+ * nm_setting_get_name:
+ * @setting: the #NMSetting
+ *
+ * Returns the type name of the #NMSetting object
+ *
+ * Returns: a string containing the type name of the #NMSetting object,
+ * like 'ppp' or 'wireless' or 'wired'.
+ **/
+const char *
+nm_setting_get_name (NMSetting *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	return NM_SETTING_GET_PRIVATE (setting)->name;
+}
+
+/**
+ * nm_setting_verify:
+ * @setting: the #NMSetting to verify
+ * @all_settings: a #GSList of all settings in the connection from which @setting
+ * came
+ * @error: location to store error, or %NULL
+ *
+ * Validates the setting.  Each setting's properties have allowed values, and
+ * some are dependent on other values (hence the need for @all_settings).  The
+ * returned #GError contains information about which property of the setting
+ * failed validation, and in what way that property failed validation.
+ *
+ * Returns: %TRUE if the setting is valid, %FALSE if it is not
+ **/
+gboolean
+nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	if (NM_SETTING_GET_CLASS (setting)->verify)
+		return NM_SETTING_GET_CLASS (setting)->verify (setting, all_settings, error);
+
+	return TRUE;
+}
+
+/**
+ * nm_setting_compare:
+ * @a: a #NMSetting
+ * @b: a second #NMSetting to compare with the first
+ * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
+ *
+ * Compares two #NMSetting objects for similarity, with comparison behavior
+ * modified by a set of flags.  See the documentation for #NMSettingCompareFlags
+ * for a description of each flag's behavior.
+ *
+ * Returns: %TRUE if the comparison succeeds, %FALSE if it does not
+ **/
+gboolean
+nm_setting_compare (NMSetting *a,
+                    NMSetting *b,
+                    NMSettingCompareFlags flags)
+{
+	GParamSpec **property_specs;
+	guint n_property_specs;
+	gint different;
+	guint i;
+
+	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
+	g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
+
+	/* First check that both have the same type */
+	if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
+		return FALSE;
+
+	/* And now all properties */
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
+	different = FALSE;
+
+	for (i = 0; i < n_property_specs && !different; i++) {
+		GParamSpec *prop_spec = property_specs[i];
+		GValue value1 = { 0 };
+		GValue value2 = { 0 };
+
+		/* Fuzzy compare ignores secrets and properties defined with the
+		 * FUZZY_IGNORE flag
+		 */
+		if (   (flags & NM_SETTING_COMPARE_FLAG_FUZZY)
+		    && (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
+			continue;
+
+		if ((flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS) && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
+			continue;
+
+		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
+			   && !strcmp (nm_setting_get_name (a), NM_SETTING_CONNECTION_SETTING_NAME)
+		    && !strcmp (prop_spec->name, NM_SETTING_CONNECTION_ID))
+			continue;
+
+		g_value_init (&value1, prop_spec->value_type);
+		g_object_get_property (G_OBJECT (a), prop_spec->name, &value1);
+
+		g_value_init (&value2, prop_spec->value_type);
+		g_object_get_property (G_OBJECT (b), prop_spec->name, &value2);
+
+		different = g_param_values_cmp (prop_spec, &value1, &value2);
+
+		g_value_unset (&value1);
+		g_value_unset (&value2);
+	}
+
+	g_free (property_specs);
+
+	return different == 0 ? TRUE : FALSE;
+}
+
+/**
+ * nm_setting_enumerate_values:
+ * @setting: the #NMSetting
+ * @func: user-supplied function called for each property of the setting
+ * @user_data: user data passed to @func at each invocation
+ *
+ * Iterates over each property of the #NMSetting object, calling the supplied
+ * user function for each property.
+ **/
+void
+nm_setting_enumerate_values (NMSetting *setting,
+					    NMSettingValueIterFn func,
+					    gpointer user_data)
+{
+	GParamSpec **property_specs;
+	guint n_property_specs;
+	int i;
+
+	g_return_if_fail (NM_IS_SETTING (setting));
+	g_return_if_fail (func != NULL);
+
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+	for (i = 0; i < n_property_specs; i++) {
+		GParamSpec *prop_spec = property_specs[i];
+		GValue value = { 0 };
+
+		g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec));
+		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+		func (setting, prop_spec->name, &value, prop_spec->flags, user_data);
+		g_value_unset (&value);
+	}
+
+	g_free (property_specs);
+}
+
+/**
+ * nm_setting_clear_secrets:
+ * @setting: the #NMSetting
+ *
+ * Resets and clears any secrets in the setting.  Secrets should be added to the
+ * setting only when needed, and cleared immediately after use to prevent
+ * leakage of information.
+ **/
+void
+nm_setting_clear_secrets (NMSetting *setting)
+{
+	GParamSpec **property_specs;
+	guint n_property_specs;
+	guint i;
+
+	g_return_if_fail (NM_IS_SETTING (setting));
+
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+
+	for (i = 0; i < n_property_specs; i++) {
+		GParamSpec *prop_spec = property_specs[i];
+		GValue value = { 0 };
+
+		if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
+			g_value_init (&value, prop_spec->value_type);
+			g_param_value_set_default (prop_spec, &value);
+			g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
+			g_value_unset (&value);
+		}
+	}
+
+	g_free (property_specs);
+}
+
+/**
+ * nm_setting_need_secrets:
+ * @setting: the #NMSetting
+ *
+ * Returns an array of property names for each secret which may be required
+ * to make a successful connection.  The returned hints are only intended as a
+ * guide to what secrets may be required, because in some circumstances, there
+ * is no way to conclusively determine exactly which secrets are needed.
+ *
+ * Returns: a #GPtrArray containing the property names of secrets of the
+ * #NMSetting which may be required; the caller owns the array
+ * and must free the each array element with g_free(), as well as the array
+ * itself with g_ptr_array_free()
+ **/
+GPtrArray *
+nm_setting_need_secrets (NMSetting *setting)
+{
+	GPtrArray *secrets = NULL;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	if (NM_SETTING_GET_CLASS (setting)->need_secrets)
+		secrets = NM_SETTING_GET_CLASS (setting)->need_secrets (setting);
+
+	return secrets;
+}
+
+typedef struct {
+	NMSetting *setting;
+	GError **error;
+} UpdateSecretsInfo;
+
+static gboolean
+update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
+{
+	GParamSpec *prop_spec;
+	GValue transformed_value = { 0 };
+	gboolean success = FALSE;
+
+	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
+	if (!prop_spec) {
+		g_set_error (error,
+		             NM_SETTING_ERROR,
+		             NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
+		             "%s", key);
+		return FALSE;
+	}
+
+	if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET)) {
+		g_set_error (error,
+		             NM_SETTING_ERROR,
+		             NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
+		             "%s", key);
+		return FALSE;
+	}
+
+	if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) {
+		g_object_set_property (G_OBJECT (setting), prop_spec->name, value);
+		success = TRUE;
+	} else if (g_value_transform (value, &transformed_value)) {
+		g_object_set_property (G_OBJECT (setting), prop_spec->name, &transformed_value);
+		g_value_unset (&transformed_value);
+		success = TRUE;
+	} else {
+		g_set_error (error,
+		             NM_SETTING_ERROR,
+		             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+		             "%s", key);
+	}
+	return success;
+}
+
+static void
+update_one_cb (gpointer key, gpointer val, gpointer user_data)
+{
+	UpdateSecretsInfo *info = user_data;
+	const char *secret_key = (const char *) key;
+	GValue *secret_value = (GValue *) val;
+
+	if (*(info->error) == NULL)
+		NM_SETTING_GET_CLASS (info->setting)->update_one_secret (info->setting, secret_key, secret_value, info->error);
+}
+
+/**
+ * nm_setting_update_secrets:
+ * @setting: the #NMSetting
+ * @secrets: a #GHashTable mapping string:#GValue of setting property names and
+ * secrets
+ * @error: location to store error, or %NULL
+ *
+ * Update the setting's secrets, given a hash table of secrets intended for that
+ * setting (deserialized from D-Bus for example).
+ * 
+ * Returns: %TRUE if the secrets were successfully updated and the connection
+ * is valid, %FALSE on failure or if the setting was never added to the connection
+ **/
+gboolean
+nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error)
+{
+	UpdateSecretsInfo *info;
+	gboolean success;
+
+	g_return_val_if_fail (setting != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+	g_return_val_if_fail (secrets != NULL, FALSE);
+	if (error)
+		g_return_val_if_fail (*error == NULL, FALSE);
+
+	info = g_malloc0 (sizeof (UpdateSecretsInfo));
+	info->setting = setting;
+	info->error = error;
+	g_hash_table_foreach (secrets, update_one_cb, info);
+	success = *(info->error) ? FALSE : TRUE;
+	g_free (info);
+
+	return success;
+}
+
+/**
+ * nm_setting_to_string:
+ * @setting: the #NMSetting
+ *
+ * Convert the setting into a string.  For debugging purposes ONLY, should NOT
+ * be used for serialization of the setting, or machine-parsed in any way. The
+ * output format is not guaranteed to be stable and may change at any time.
+ *
+ * Returns: an allocated string containing a textual representation of the
+ * setting's properties and values (including secrets!), which the caller should
+ * free with g_free()
+ **/
+char *
+nm_setting_to_string (NMSetting *setting)
+{
+	GString *string;
+	GParamSpec **property_specs;
+	guint n_property_specs;
+	guint i;
+
+	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+	if (!property_specs)
+		return NULL;
+
+	string = g_string_new (nm_setting_get_name (setting));
+	g_string_append_c (string, '\n');
+
+	for (i = 0; i < n_property_specs; i++) {
+		GParamSpec *prop_spec = property_specs[i];
+		GValue value = { 0 };
+		char *value_str;
+		gboolean is_serializable;
+		gboolean is_default;
+
+		g_value_init (&value, prop_spec->value_type);
+		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+
+		value_str = g_strdup_value_contents (&value);
+		g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str);
+		g_free (value_str);
+
+		is_serializable = prop_spec->flags & NM_SETTING_PARAM_SERIALIZE;
+		is_default = g_param_value_defaults (prop_spec, &value);
+
+		if (is_serializable || is_default) {
+			g_string_append (string, " (");
+
+			if (is_serializable)
+				g_string_append_c (string, 's');
+			if (is_default)
+				g_string_append_c (string, 'd');
+
+			g_string_append_c (string, ')');
+		}
+
+		g_string_append_c (string, '\n');
+	}
+
+	g_free (property_specs);
+	g_string_append_c (string, '\n');
+
+	return g_string_free (string, FALSE);
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_init (NMSetting *setting)
+{
+}
+
+static GObject*
+constructor (GType type,
+		   guint n_construct_params,
+		   GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	NMSettingPrivate *priv;
+
+	object = G_OBJECT_CLASS (nm_setting_parent_class)->constructor (type,
+													    n_construct_params,
+													    construct_params);
+	if (!object)
+		return NULL;
+
+	priv = NM_SETTING_GET_PRIVATE (object);
+	if (!priv->name) {
+		nm_warning ("Setting name is not set.");
+		g_object_unref (object);
+		object = NULL;
+	}
+
+	return object;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
+
+	g_free (priv->name);
+
+	G_OBJECT_CLASS (nm_setting_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_NAME:
+		g_free (priv->name);
+		priv->name = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+		    GValue *value, GParamSpec *pspec)
+{
+	NMSetting *setting = NM_SETTING (object);
+
+	switch (prop_id) {
+	case PROP_NAME:
+		g_value_set_string (value, nm_setting_get_name (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_setting_class_init (NMSettingClass *setting_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+
+	g_type_class_add_private (setting_class, sizeof (NMSettingPrivate));
+
+	/* virtual methods */
+	object_class->constructor  = constructor;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize     = finalize;
+
+	setting_class->update_one_secret = update_one_secret;
+
+	/* Properties */
+
+	/**
+	 * NMSetting:name:
+	 *
+	 * The setting's name, which uniquely identifies the setting within the
+	 * connection.  Each setting type has a name unique to that type, for
+	 * example 'ppp' or 'wireless' or 'wired'.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_NAME,
+		 g_param_spec_string (NM_SETTING_NAME,
+						  "Name",
+						  "The setting's name; these names are defined by the "
+						  "specification and cannot be changed after the object "
+						  "has been created.  Each setting class has a name, and "
+						  "all objects of that class share the same name.",
+						  NULL,
+						  G_PARAM_READWRITE));
+}
+
diff --git a/libnm-util/nm-setting.h b/libnm-util/nm-setting.h
new file mode 100644
index 00000000..de5657ee
--- /dev/null
+++ b/libnm-util/nm-setting.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2008 Red Hat, Inc.
+ * (C) Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_H
+#define NM_SETTING_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING            (nm_setting_get_type ())
+#define NM_SETTING(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING, NMSetting))
+#define NM_SETTING_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING, NMSettingClass))
+#define NM_IS_SETTING(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING))
+#define NM_IS_SETTING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING))
+#define NM_SETTING_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING, NMSettingClass))
+
+/**
+ * NMSettingError:
+ * @NM_SETTING_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_ERROR_PROPERTY_NOT_FOUND: a property required by the operation
+ *   was not found; for example, an attempt to update an invalid secret
+ * @NM_SETTING_ERROR_PROPERTY_NOT_SECRET: an operation which requires a secret
+ *   was attempted on a non-secret property
+ * @NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH: the operation requires a property
+ *   of a specific type, or the value couldn't be transformed to the same type
+ *   as the property being acted upon
+ *
+ * Describes errors that may result from operations involving a #NMSetting.
+ *
+ **/
+typedef enum
+{
+	NM_SETTING_ERROR_UNKNOWN = 0,
+	NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
+	NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
+	NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH
+} NMSettingError;
+
+#define NM_TYPE_SETTING_ERROR (nm_setting_error_get_type ()) 
+GType nm_setting_error_get_type (void);
+
+#define NM_SETTING_ERROR nm_setting_error_quark ()
+GQuark nm_setting_error_quark (void);
+
+
+/* The property of the #NMSetting should be serialized */
+#define NM_SETTING_PARAM_SERIALIZE    (1 << (0 + G_PARAM_USER_SHIFT))
+
+/* The property of the #NMSetting is required for the setting to be valid */
+#define NM_SETTING_PARAM_REQUIRED     (1 << (1 + G_PARAM_USER_SHIFT))
+
+/* The property of the #NMSetting is a secret */
+#define NM_SETTING_PARAM_SECRET       (1 << (2 + G_PARAM_USER_SHIFT))
+
+/* The property of the #NMSetting should be ignored during comparisons that
+ * use the %NM_SETTING_COMPARE_FLAG_FUZZY flag.
+ */
+#define NM_SETTING_PARAM_FUZZY_IGNORE (1 << (3 + G_PARAM_USER_SHIFT))
+
+#define NM_SETTING_NAME "name"
+
+/**
+ * NMSetting:
+ *
+ * The NMSetting struct contains only private data.
+ * It should only be accessed through the functions described below.
+ */
+typedef struct {
+	GObject parent;
+} NMSetting;
+
+typedef struct {
+	GObjectClass parent;
+
+	/* Virtual functions */
+	gboolean    (*verify)            (NMSetting  *setting,
+	                                  GSList     *all_settings,
+	                                  GError     **error);
+
+	GPtrArray  *(*need_secrets)      (NMSetting  *setting);
+
+	gboolean    (*update_one_secret) (NMSetting  *setting,
+	                                  const char *key,
+	                                  GValue     *value,
+	                                  GError    **error);
+
+	/* Padding for future expansion */
+	void (*_reserved1) (void);
+	void (*_reserved2) (void);
+	void (*_reserved3) (void);
+	void (*_reserved4) (void);
+} NMSettingClass;
+
+typedef void (*NMSettingValueIterFn) (NMSetting *setting,
+                                      const char *key,
+                                      const GValue *value,
+                                      GParamFlags flags,
+                                      gpointer user_data);
+
+
+GType nm_setting_get_type (void);
+
+GHashTable *nm_setting_to_hash       (NMSetting *setting);
+NMSetting  *nm_setting_new_from_hash (GType setting_type,
+                                      GHashTable *hash);
+
+NMSetting *nm_setting_duplicate      (NMSetting *setting);
+
+const char *nm_setting_get_name      (NMSetting *setting);
+
+gboolean    nm_setting_verify        (NMSetting *setting,
+                                      GSList    *all_settings,
+                                      GError    **error);
+
+/**
+ * NMSettingCompareFlags:
+ * @NM_SETTING_COMPARE_FLAG_EXACT: match all properties exactly
+ * @NM_SETTING_COMPARE_FLAG_FUZZY: match only important attributes, like SSID,
+ *   type, security settings, etc.  Does not match, for example, connection ID
+ *   or UUID.
+ * @NM_SETTING_COMPARE_FLAG_IGNORE_ID: ignore the connection's ID
+ * @NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS: ignore secrets
+ *
+ * These flags modify the comparison behavior when comparing two settings or
+ * two connections.
+ *
+ **/
+typedef enum {
+	NM_SETTING_COMPARE_FLAG_EXACT = 0x00000000,
+	NM_SETTING_COMPARE_FLAG_FUZZY = 0x00000001,
+	NM_SETTING_COMPARE_FLAG_IGNORE_ID = 0x00000002,
+	NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS = 0x00000004
+} NMSettingCompareFlags;
+
+gboolean    nm_setting_compare       (NMSetting *a,
+                                      NMSetting *b,
+                                      NMSettingCompareFlags flags);
+
+void        nm_setting_enumerate_values (NMSetting *setting,
+                                         NMSettingValueIterFn func,
+                                         gpointer user_data);
+
+char       *nm_setting_to_string      (NMSetting *setting);
+
+/* Secrets */
+void        nm_setting_clear_secrets  (NMSetting *setting);
+GPtrArray  *nm_setting_need_secrets   (NMSetting *setting);
+gboolean    nm_setting_update_secrets (NMSetting *setting,
+                                       GHashTable *secrets,
+                                       GError **error);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_H */
+
diff --git a/libnm-util/nm-utils-private.h b/libnm-util/nm-utils-private.h
new file mode 100644
index 00000000..fa1e3a01
--- /dev/null
+++ b/libnm-util/nm-utils-private.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * Ray Strode <rstrode@redhat.com>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2005 - 2008 Red Hat, Inc.
+ */
+
+#ifndef __NM_UTILS_PRIVATE_H__
+#define __NM_UTILS_PRIVATE_H__
+
+gboolean    _nm_utils_string_in_list   (const char *str,
+                                        const char **valid_strings);
+
+gboolean    _nm_utils_string_slist_validate (GSList *list,
+                                             const char **valid_values);
+
+void        _nm_utils_register_value_transformations (void);
+
+#endif
diff --git a/libnm-util/nm-utils.c b/libnm-util/nm-utils.c
new file mode 100644
index 00000000..ddb02a22
--- /dev/null
+++ b/libnm-util/nm-utils.c
@@ -0,0 +1,2104 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* NetworkManager -- Network link manager
+ *
+ * Ray Strode <rstrode@redhat.com>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2005 - 2010 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include "wireless-helper.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus-glib.h>
+#include <uuid/uuid.h>
+
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "NetworkManager.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "crypto.h"
+
+/**
+ * SECTION:nm-utils
+ * @short_description: Utility functions
+ * @include: nm-utils.h
+ *
+ * A collection of utility functions for working SSIDs, IP addresses, WiFi
+ * access points and devices, among other things.
+ */
+
+struct EncodingTriplet
+{
+	const char *encoding1;
+	const char *encoding2;
+	const char *encoding3;
+};
+
+struct IsoLangToEncodings
+{
+	const char *	lang;
+	struct EncodingTriplet encodings;
+};
+
+/* 5-letter language codes */
+static const struct IsoLangToEncodings isoLangEntries5[] =
+{
+	/* Simplified Chinese */
+	{ "zh_cn",	{"euc-cn",	"gb2312",			"gb18030"} },	/* PRC */
+	{ "zh_sg",	{"euc-cn",	"gb2312",			"gb18030"} },	/* Singapore */
+
+	/* Traditional Chinese */
+	{ "zh_tw",	{"big5",		"euc-tw",			NULL} },		/* Taiwan */
+	{ "zh_hk",	{"big5",		"euc-tw",			"big5-hkcs"} },/* Hong Kong */
+	{ "zh_mo",	{"big5",		"euc-tw",			NULL} },		/* Macau */
+
+	/* Table end */
+	{ NULL, {NULL, NULL, NULL} }
+};
+
+/* 2-letter language codes; we don't care about the other 3 in this table */
+static const struct IsoLangToEncodings isoLangEntries2[] =
+{
+	/* Japanese */
+	{ "ja",		{"euc-jp",	"shift_jis",		"iso-2022-jp"} },
+
+	/* Korean */
+	{ "ko",		{"euc-kr",	"iso-2022-kr",		"johab"} },
+
+	/* Thai */
+	{ "th",		{"iso-8859-11","windows-874",		NULL} },
+
+	/* Central European */
+	{ "hu",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Hungarian */
+	{ "cs",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Czech */
+	{ "hr",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Croatian */
+	{ "pl",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Polish */
+	{ "ro",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Romanian */
+	{ "sk",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Slovakian */
+	{ "sl",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Slovenian */
+	{ "sh",		{"iso-8859-2",	"windows-1250",	NULL} },	/* Serbo-Croatian */
+
+	/* Cyrillic */
+	{ "ru",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Russian */
+	{ "be",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Belorussian */
+	{ "bg",		{"windows-1251","koi8-r",		"iso-8859-5"} },	/* Bulgarian */
+	{ "mk",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Macedonian */
+	{ "sr",		{"koi8-r",	"windows-1251",	"iso-8859-5"} },	/* Serbian */
+	{ "uk",		{"koi8-u",	"koi8-r",			"windows-1251"} },	/* Ukranian */
+
+	/* Arabic */
+	{ "ar",		{"iso-8859-6",	"windows-1256",	NULL} },
+
+	/* Balitc */
+	{ "et",		{"iso-8859-4",	"windows-1257",	NULL} },	/* Estonian */
+	{ "lt",		{"iso-8859-4",	"windows-1257",	NULL} },	/* Lithuanian */
+	{ "lv",		{"iso-8859-4",	"windows-1257",	NULL} },	/* Latvian */
+
+	/* Greek */
+	{ "el",		{"iso-8859-7",	"windows-1253",	NULL} },
+
+	/* Hebrew */
+	{ "he",		{"iso-8859-8",	"windows-1255",	NULL} },
+	{ "iw",		{"iso-8859-8",	"windows-1255",	NULL} },
+
+	/* Turkish */
+	{ "tr",		{"iso-8859-9",	"windows-1254",	NULL} },
+
+	/* Table end */
+	{ NULL, {NULL, NULL, NULL} }
+};
+
+
+static GHashTable * langToEncodings5 = NULL;
+static GHashTable * langToEncodings2 = NULL;
+
+static void
+init_lang_to_encodings_hash (void)
+{
+	struct IsoLangToEncodings *enc;
+
+	if (G_UNLIKELY (langToEncodings5 == NULL)) {
+		/* Five-letter codes */
+		enc = (struct IsoLangToEncodings *) &isoLangEntries5[0];
+		langToEncodings5 = g_hash_table_new (g_str_hash, g_str_equal);
+		while (enc->lang) {
+			g_hash_table_insert (langToEncodings5, (gpointer) enc->lang,
+					(gpointer) &enc->encodings);
+			enc++;
+		}
+	}
+
+	if (G_UNLIKELY (langToEncodings2 == NULL)) {
+		/* Two-letter codes */
+		enc = (struct IsoLangToEncodings *) &isoLangEntries2[0];
+		langToEncodings2 = g_hash_table_new (g_str_hash, g_str_equal);
+		while (enc->lang) {
+			g_hash_table_insert (langToEncodings2, (gpointer) enc->lang,
+					(gpointer) &enc->encodings);
+			enc++;
+		}
+	}
+}
+
+
+static gboolean
+get_encodings_for_lang (const char *lang,
+                        char **encoding1,
+                        char **encoding2,
+                        char **encoding3)
+{
+	struct EncodingTriplet *	encodings;
+	gboolean				success = FALSE;
+	char *				tmp_lang;
+
+	g_return_val_if_fail (lang != NULL, FALSE);
+	g_return_val_if_fail (encoding1 != NULL, FALSE);
+	g_return_val_if_fail (encoding2 != NULL, FALSE);
+	g_return_val_if_fail (encoding3 != NULL, FALSE);
+
+	*encoding1 = "iso-8859-1";
+	*encoding2 = "windows-1251";
+	*encoding3 = NULL;
+
+	init_lang_to_encodings_hash ();
+
+	tmp_lang = g_strdup (lang);
+	if ((encodings = g_hash_table_lookup (langToEncodings5, tmp_lang)))
+	{
+		*encoding1 = (char *) encodings->encoding1;
+		*encoding2 = (char *) encodings->encoding2;
+		*encoding3 = (char *) encodings->encoding3;
+		success = TRUE;
+	}
+
+	/* Truncate tmp_lang to length of 2 */
+	if (strlen (tmp_lang) > 2)
+		tmp_lang[2] = '\0';
+	if (!success && (encodings = g_hash_table_lookup (langToEncodings2, tmp_lang)))
+	{
+		*encoding1 = (char *) encodings->encoding1;
+		*encoding2 = (char *) encodings->encoding2;
+		*encoding3 = (char *) encodings->encoding3;
+		success = TRUE;
+	}
+
+	g_free (tmp_lang);
+	return success;
+}
+
+static char *
+string_to_utf8 (const char *str, gsize len)
+{
+	char *converted = NULL;
+	char *lang, *e1 = NULL, *e2 = NULL, *e3 = NULL;
+
+	g_return_val_if_fail (str != NULL, NULL);
+
+	if (g_utf8_validate (str, len, NULL))
+		return g_strdup (str);
+
+	/* LANG may be a good encoding hint */
+	g_get_charset ((const char **)(&e1));
+	if ((lang = getenv ("LANG"))) {
+		char * dot;
+
+		lang = g_ascii_strdown (lang, -1);
+		if ((dot = strchr (lang, '.')))
+			*dot = '\0';
+
+		get_encodings_for_lang (lang, &e1, &e2, &e3);
+		g_free (lang);
+	}
+
+	converted = g_convert (str, len, "UTF-8", e1, NULL, NULL, NULL);
+	if (!converted && e2)
+		converted = g_convert (str, len, "UTF-8", e2, NULL, NULL, NULL);
+
+	if (!converted && e3)
+		converted = g_convert (str, len, "UTF-8", e3, NULL, NULL, NULL);
+
+	if (!converted) {
+		converted = g_convert_with_fallback (str, len, "UTF-8", e1,
+	                "?", NULL, NULL, NULL);
+	}
+
+	return converted;
+}
+
+/* init, deinit for libnm_util */
+
+static gboolean initialized = FALSE;
+
+/**
+ * nm_utils_init:
+ * @error: location to store error, or %NULL
+ *
+ * Initializes libnm-util; should be called when starting and program that
+ * uses libnm-util.  Sets up an atexit() handler to ensure de-initialization
+ * is performed, but calling nm_utils_deinit() to explicitly deinitialize
+ * libnm-util can also be done.  This function can be called more than once.
+ * 
+ * Returns: TRUE if the initialization was successful, FALSE on failure.
+ **/
+gboolean
+nm_utils_init (GError **error)
+{
+	if (!initialized) {
+		if (!crypto_init (error))
+			return FALSE;
+
+		_nm_utils_register_value_transformations ();
+
+		atexit (nm_utils_deinit);
+		initialized = TRUE;
+	}
+	return TRUE;
+}
+
+/**
+ * nm_utils_deinit:
+ *
+ * Frees all resources used internally by libnm-util.  This function is called
+ * from an atexit() handler, set up by nm_utils_init(), but is safe to be called
+ * more than once.  Subsequent calls have no effect until nm_utils_init() is
+ * called again.
+ **/
+void
+nm_utils_deinit (void)
+{
+	if (initialized) {
+		crypto_deinit ();
+		initialized = FALSE;
+	}
+}
+
+/* ssid helpers */
+
+/**
+ * nm_utils_ssid_to_utf8:
+ * @ssid: pointer to a buffer containing the SSID data
+ * @len: length of the SSID data in @ssid
+ *
+ * WiFi SSIDs are byte arrays, they are _not_ strings.  Thus, an SSID may
+ * contain embedded NULLs and other unprintable characters.  Often it is
+ * useful to print the SSID out for debugging purposes, but that should be the
+ * _only_ use of this function.  Do not use this function for any persistent
+ * storage of the SSID, since the printable SSID returned from this function
+ * cannot be converted back into the real SSID of the access point.
+ *
+ * This function does almost everything humanly possible to convert the input
+ * into a printable UTF-8 string, using roughly the following procedure:
+ *
+ * 1) if the input data is already UTF-8 safe, no conversion is performed
+ * 2) attempts to get the current system language from the LANG environment
+ *    variable, and depending on the language, uses a table of alternative
+ *    encodings to try.  For example, if LANG=hu_HU, the table may first try
+ *    the ISO-8859-2 encoding, and if that fails, try the Windows-1250 encoding.
+ *    If all fallback encodings fail, replaces non-UTF-8 characters with '?'.
+ * 3) If the system language was unable to be determined, falls back to the
+ *    ISO-8859-1 encoding, then to the Windows-1251 encoding.
+ * 4) If step 3 fails, replaces non-UTF-8 characters with '?'.
+ *
+ * Again, this function should be used for debugging and display purposes
+ * _only_.
+ *
+ * Returns: an allocated string containing a UTF-8 representation of the
+ * SSID, which must be freed by the caller using g_free().  Returns NULL
+ * on errors.
+ **/
+char *
+nm_utils_ssid_to_utf8 (const char *ssid, guint32 len)
+{
+	char *converted = NULL, *buf;
+	gsize buflen = MIN (IW_ESSID_MAX_SIZE, (gsize) len);
+
+	g_return_val_if_fail (ssid != NULL, NULL);
+
+	/* New buffer to ensure NULL-termination of SSID */
+	buf = g_malloc0 (IW_ESSID_MAX_SIZE + 1);
+	memcpy (buf, ssid, buflen);
+	converted = string_to_utf8 (buf, buflen);
+	g_free (buf);
+	return converted;
+}
+
+/* Shamelessly ripped from the Linux kernel ieee80211 stack */
+/**
+ * nm_utils_is_empty_ssid:
+ * @ssid: pointer to a buffer containing the SSID data
+ * @len: length of the SSID data in @ssid
+ *
+ * Different manufacturers use different mechanisms for not broadcasting the
+ * AP's SSID.  This function attempts to detect blank/empty SSIDs using a
+ * number of known SSID-cloaking methods.
+ *
+ * Returns: TRUE if the SSID is "empty", FALSE if it is not
+ **/
+gboolean
+nm_utils_is_empty_ssid (const guint8 * ssid, int len)
+{
+        /* Single white space is for Linksys APs */
+        if (len == 1 && ssid[0] == ' ')
+                return TRUE;
+
+        /* Otherwise, if the entire ssid is 0, we assume it is hidden */
+        while (len--) {
+                if (ssid[len] != '\0')
+                        return FALSE;
+        }
+        return TRUE;
+}
+
+/**
+ * nm_utils_escape_ssid:
+ * @ssid: pointer to a buffer containing the SSID data
+ * @len: length of the SSID data in @ssid
+ *
+ * This function does a quick printable character conversion of the SSID, simply
+ * replacing embedded NULLs and non-printable characters with the hexadecimal
+ * representation of that character.  Intended for debugging only, should not
+ * be used for display of SSIDs.
+ *
+ * Returns: pointer to the escaped SSID, which uses an internal static buffer
+ * and will be overwritten by subsequent calls to this function
+ **/
+const char *
+nm_utils_escape_ssid (const guint8 * ssid, guint32 len)
+{
+	static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+	const guint8 *s = ssid;
+	char *d = escaped;
+
+	if (nm_utils_is_empty_ssid (ssid, len)) {
+		memcpy (escaped, "<hidden>", sizeof ("<hidden>"));
+		return escaped;
+	}
+
+	len = MIN (len, (guint32) IW_ESSID_MAX_SIZE);
+	while (len--) {
+		if (*s == '\0') {
+			*d++ = '\\';
+			*d++ = '0';
+			s++;
+		} else {
+			*d++ = *s++;
+		}
+	}
+	*d = '\0';
+	return escaped;
+}
+
+/**
+ * nm_utils_same_ssid:
+ * @ssid1: first SSID data to compare
+ * @ssid2: second SSID data to compare
+ * @ignore_trailing_null: TRUE to ignore one trailing NULL byte
+ *
+ * Earlier versions of the Linux kernel added a NULL byte to the end of the
+ * SSID to enable easy printing of the SSID on the console or in a terminal,
+ * but this behavior was problematic (SSIDs are simply byte arrays, not strings)
+ * and thus was changed.  This function compensates for that behavior at the
+ * cost of some compatibility with odd SSIDs that may legitimately have trailing
+ * NULLs, even though that is functionally pointless.
+ *
+ * Returns: TRUE if the SSIDs are the same, FALSE if they are not
+ **/
+gboolean
+nm_utils_same_ssid (const GByteArray * ssid1,
+                    const GByteArray * ssid2,
+                    gboolean ignore_trailing_null)
+{
+	guint32 ssid1_len, ssid2_len;
+
+	if (ssid1 == ssid2)
+		return TRUE;
+	if ((ssid1 && !ssid2) || (!ssid1 && ssid2))
+		return FALSE;
+
+	ssid1_len = ssid1->len;
+	ssid2_len = ssid2->len;
+	if (ssid1_len && ssid2_len && ignore_trailing_null) {
+		if (ssid1->data[ssid1_len - 1] == '\0')
+			ssid1_len--;
+		if (ssid2->data[ssid2_len - 1] == '\0')
+			ssid2_len--;
+	}
+
+	if (ssid1_len != ssid2_len)
+		return FALSE;
+
+	return memcmp (ssid1->data, ssid2->data, ssid1_len) == 0 ? TRUE : FALSE;
+}
+
+static void
+value_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static void
+value_dup (gpointer key, gpointer val, gpointer user_data)
+{
+	GHashTable *table = (GHashTable *) user_data;
+	GValue *value = (GValue *) val;
+	GValue *dup_value;
+
+	dup_value = g_slice_new0 (GValue);
+	g_value_init (dup_value, G_VALUE_TYPE (val));
+	g_value_copy (value, dup_value);
+
+	g_hash_table_insert (table, g_strdup ((char *) key), dup_value);
+}
+
+/**
+ * nm_utils_gvalue_hash_dup:
+ * @hash: a #GHashTable mapping string:GValue
+ *
+ * Utility function to duplicate a hash table of GValues.
+ *
+ * Returns: a newly allocated duplicated #GHashTable, caller must free the
+ * returned hash with g_hash_table_unref() or g_hash_table_destroy()
+ **/
+GHashTable *
+nm_utils_gvalue_hash_dup (GHashTable *hash)
+{
+	GHashTable *table;
+
+	g_return_val_if_fail (hash != NULL, NULL);
+
+	table = g_hash_table_new_full (g_str_hash, g_str_equal,
+						    (GDestroyNotify) g_free,
+						    value_destroy);
+
+	g_hash_table_foreach (hash, value_dup, table);
+
+	return table;
+}
+
+/**
+ * nm_utils_slist_free:
+ * @list: a #GSList
+ * @elem_destroy_fn: user function called for each element in @list
+ *
+ * Utility function to free a #GSList.
+ **/
+void
+nm_utils_slist_free (GSList *list, GDestroyNotify elem_destroy_fn)
+{
+	if (!list)
+		return;
+
+	if (elem_destroy_fn)
+		g_slist_foreach (list, (GFunc) elem_destroy_fn, NULL);
+
+	g_slist_free (list);
+}
+
+gboolean
+_nm_utils_string_in_list (const char *str, const char **valid_strings)
+{
+	int i;
+
+	for (i = 0; valid_strings[i]; i++)
+		if (strcmp (str, valid_strings[i]) == 0)
+			break;
+
+	return valid_strings[i] != NULL;
+}
+
+gboolean
+_nm_utils_string_slist_validate (GSList *list, const char **valid_values)
+{
+	GSList *iter;
+
+	for (iter = list; iter; iter = iter->next) {
+		if (!_nm_utils_string_in_list ((char *) iter->data, valid_values))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+nm_utils_convert_strv_to_slist (const GValue *src_value, GValue *dest_value)
+{
+	char **str;
+	GSList *list = NULL;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV));
+
+	str = (char **) g_value_get_boxed (src_value);
+
+	while (str && str[i])
+		list = g_slist_prepend (list, g_strdup (str[i++]));
+
+	g_value_take_boxed (dest_value, g_slist_reverse (list));
+}
+
+static void
+nm_utils_convert_strv_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GSList *strings;
+	GString *printable;
+	GSList *iter;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_LIST_OF_STRING));
+
+	strings = (GSList *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	for (iter = strings; iter; iter = g_slist_next (iter)) {
+		if (iter != strings)
+			g_string_append (printable, ", '");
+		else
+			g_string_append_c (printable, '\'');
+		g_string_append (printable, iter->data);
+		g_string_append_c (printable, '\'');
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+nm_utils_convert_uint_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GArray *array;
+	GString *printable;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UINT_ARRAY));
+
+	array = (GArray *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	while (array && (i < array->len)) {
+		char buf[INET_ADDRSTRLEN + 1];
+		struct in_addr addr;
+
+		if (i > 0)
+			g_string_append (printable, ", ");
+
+		memset (buf, 0, sizeof (buf));
+		addr.s_addr = g_array_index (array, guint32, i++);
+		if (!inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN))
+			nm_warning ("%s: error converting IP4 address 0x%X",
+			            __func__, ntohl (addr.s_addr));
+		g_string_append_printf (printable, "%u (%s)", addr.s_addr, buf);
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+nm_utils_convert_ip4_addr_route_struct_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GPtrArray *ptr_array;
+	GString *printable;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT));
+
+	ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	while (ptr_array && (i < ptr_array->len)) {
+		GArray *array;
+		char buf[INET_ADDRSTRLEN + 1];
+		struct in_addr addr;
+		gboolean is_addr; /* array contains address x route */
+
+		if (i > 0)
+			g_string_append (printable, ", ");
+
+		g_string_append (printable, "{ ");
+		array = (GArray *) g_ptr_array_index (ptr_array, i++);
+		if (array->len < 2) {
+			g_string_append (printable, "invalid");
+			continue;
+		}
+		is_addr = (array->len < 4);
+
+		memset (buf, 0, sizeof (buf));
+		addr.s_addr = g_array_index (array, guint32, 0);
+		if (!inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN))
+			nm_warning ("%s: error converting IP4 address 0x%X",
+			            __func__, ntohl (addr.s_addr));
+		if (is_addr)
+			g_string_append_printf (printable, "ip = %s", buf);
+		else
+			g_string_append_printf (printable, "dst = %s", buf);
+		g_string_append (printable, ", ");
+
+		memset (buf, 0, sizeof (buf));
+		g_string_append_printf (printable, "px = %u",
+		                        g_array_index (array, guint32, 1));
+
+		if (array->len > 2) {
+			g_string_append (printable, ", ");
+
+			memset (buf, 0, sizeof (buf));
+			addr.s_addr = g_array_index (array, guint32, 2);
+			if (!inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN))
+				nm_warning ("%s: error converting IP4 address 0x%X",
+				            __func__, ntohl (addr.s_addr));
+			if (is_addr)
+				g_string_append_printf (printable, "gw = %s", buf);
+			else
+				g_string_append_printf (printable, "nh = %s", buf);
+		}
+
+		if (array->len > 3) {
+			g_string_append (printable, ", ");
+
+			memset (buf, 0, sizeof (buf));
+			g_string_append_printf (printable, "mt = %u",
+			                        g_array_index (array, guint32, 3));
+		}
+
+		g_string_append (printable, " }");
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+convert_one_gvalue_hash_entry (gpointer key, gpointer value, gpointer user_data)
+{
+	GString *printable = (GString *) user_data;
+	char *value_as_string;
+
+	value_as_string = g_strdup_value_contents ((GValue *) value);
+	g_string_append_printf (printable, " { '%s': %s },", (const char *) key, value_as_string);
+	g_free (value_as_string);
+}
+
+static void
+nm_utils_convert_gvalue_hash_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GHashTable *hash;
+	GString *printable;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_VARIANT));
+
+	hash = (GHashTable *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	g_hash_table_foreach (hash, convert_one_gvalue_hash_entry, printable);
+	g_string_append (printable, " ]");
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+convert_one_string_hash_entry (gpointer key, gpointer value, gpointer user_data)
+{
+	GString *printable = (GString *) user_data;
+
+	g_string_append_printf (printable, " { '%s': %s },", (const char *) key, (const char *) value);
+}
+
+static void
+nm_utils_convert_string_hash_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GHashTable *hash;
+	GString *printable;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_STRING));
+
+	hash = (GHashTable *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	if (hash)
+		g_hash_table_foreach (hash, convert_one_string_hash_entry, printable);
+	g_string_append (printable, " ]");
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+nm_utils_convert_byte_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GArray *array;
+	GString *printable;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UCHAR_ARRAY));
+
+	array = (GArray *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	if (array) {
+		while (i < MIN (array->len, 35)) {
+			if (i > 0)
+				g_string_append_c (printable, ' ');
+			g_string_append_printf (printable, "0x%02X",
+			                        g_array_index (array, unsigned char, i++));
+		}
+		if (i < array->len)
+			g_string_append (printable, " ... ");
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static gboolean
+nm_utils_inet6_ntop (struct in6_addr *addr, char *buf)
+{
+	if (!inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN)) {
+		int i;
+		GString *ip6_str = g_string_new (NULL);
+		g_string_append_printf (ip6_str, "%02X", addr->s6_addr[0]);
+		for (i = 1; i < 16; i++)
+			g_string_append_printf (ip6_str, " %02X", addr->s6_addr[i]);
+		nm_warning ("%s: error converting IP6 address %s",
+		            __func__, ip6_str->str);
+		g_string_free (ip6_str, TRUE);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+nm_utils_convert_ip6_dns_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GPtrArray *ptr_array;
+	GString *printable;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR));
+
+	ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	while (ptr_array && (i < ptr_array->len)) {
+		GByteArray *bytearray;
+		char buf[INET6_ADDRSTRLEN];
+		struct in6_addr *addr;
+
+		if (i > 0)
+			g_string_append (printable, ", ");
+
+		bytearray = (GByteArray *) g_ptr_array_index (ptr_array, i++);
+		if (bytearray->len != 16) {
+			g_string_append (printable, "invalid");
+			continue;
+		}
+		addr = (struct in6_addr *) bytearray->data;
+		memset (buf, 0, sizeof (buf));
+		nm_utils_inet6_ntop (addr, buf);
+		g_string_append_printf (printable, "%s", buf);
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+nm_utils_convert_ip6_addr_struct_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GPtrArray *ptr_array;
+	GString *printable;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
+
+	ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	while (ptr_array && (i < ptr_array->len)) {
+		GValueArray *elements;
+		GValue *tmp;
+		GByteArray *ba_addr;
+		char buf[INET6_ADDRSTRLEN];
+		struct in6_addr *addr;
+		guint32 prefix;
+
+		if (i > 0)
+			g_string_append (printable, ", ");
+
+		g_string_append (printable, "{ ");
+		elements = (GValueArray *) g_ptr_array_index (ptr_array, i++);
+		if (   (elements->n_values != 3)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)) {
+			g_string_append (printable, "invalid }");
+			continue;
+		}
+
+		/* IPv6 address */
+		tmp = g_value_array_get_nth (elements, 0);
+		ba_addr = g_value_get_boxed (tmp);
+		if (ba_addr->len != 16) {
+			g_string_append (printable, "invalid }");
+			continue;
+		}
+		addr = (struct in6_addr *) ba_addr->data;
+		memset (buf, 0, sizeof (buf));
+		nm_utils_inet6_ntop (addr, buf);
+		g_string_append_printf (printable, "ip = %s", buf);
+		g_string_append (printable, ", ");
+
+		/* Prefix */
+		tmp = g_value_array_get_nth (elements, 1);
+		prefix = g_value_get_uint (tmp);
+		if (prefix > 128) {
+			g_string_append (printable, "invalid }");
+			continue;
+		}
+		g_string_append_printf (printable, "px = %u", prefix);
+		g_string_append (printable, ", ");
+
+		/* IPv6 Gateway */
+		tmp = g_value_array_get_nth (elements, 2);
+		ba_addr = g_value_get_boxed (tmp);
+		if (ba_addr->len != 16) {
+			g_string_append (printable, "invalid }");
+			continue;
+		}
+		addr = (struct in6_addr *) ba_addr->data;
+		memset (buf, 0, sizeof (buf));
+		nm_utils_inet6_ntop (addr, buf);
+		g_string_append_printf (printable, "gw = %s", buf);
+		g_string_append (printable, " }");
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+static void
+nm_utils_convert_ip6_route_struct_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+	GPtrArray *ptr_array;
+	GString *printable;
+	guint i = 0;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE));
+
+	ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+	printable = g_string_new ("[");
+	while (ptr_array && (i < ptr_array->len)) {
+		GValueArray *elements;
+		GValue *tmp;
+		GByteArray *ba_addr;
+		char buf[INET6_ADDRSTRLEN];
+		struct in6_addr *addr;
+		guint32 prefix, metric;
+
+		if (i > 0)
+			g_string_append (printable, ", ");
+
+		g_string_append (printable, "{ ");
+		elements = (GValueArray *) g_ptr_array_index (ptr_array, i++);
+		if (   (elements->n_values != 4)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 3)) != G_TYPE_UINT)) {
+			g_string_append (printable, "invalid");
+			continue;
+		}
+
+		/* Destination address */
+		tmp = g_value_array_get_nth (elements, 0);
+		ba_addr = g_value_get_boxed (tmp);
+		if (ba_addr->len != 16) {
+			g_string_append (printable, "invalid");
+			continue;
+		}
+		addr = (struct in6_addr *) ba_addr->data;
+		memset (buf, 0, sizeof (buf));
+		nm_utils_inet6_ntop (addr, buf);
+		g_string_append_printf (printable, "dst = %s", buf);
+		g_string_append (printable, ", ");
+
+		/* Prefix */
+		tmp = g_value_array_get_nth (elements, 1);
+		prefix = g_value_get_uint (tmp);
+		if (prefix > 128) {
+			g_string_append (printable, "invalid");
+			continue;
+		}
+		g_string_append_printf (printable, "px = %u", prefix);
+		g_string_append (printable, ", ");
+
+		/* Next hop addresses */
+		tmp = g_value_array_get_nth (elements, 2);
+		ba_addr = g_value_get_boxed (tmp);
+		if (ba_addr->len != 16) {
+			g_string_append (printable, "invalid");
+			continue;
+		}
+		addr = (struct in6_addr *) ba_addr->data;
+		memset (buf, 0, sizeof (buf));
+		nm_utils_inet6_ntop (addr, buf);
+		g_string_append_printf (printable, "nh = %s", buf);
+		g_string_append (printable, ", ");
+
+		/* Metric */
+		tmp = g_value_array_get_nth (elements, 3);
+		metric = g_value_get_uint (tmp);
+		g_string_append_printf (printable, "mt = %u", metric);
+
+		g_string_append (printable, " }");
+	}
+	g_string_append_c (printable, ']');
+
+	g_value_take_string (dest_value, printable->str);
+	g_string_free (printable, FALSE);
+}
+
+#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
+#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS))
+
+static void
+nm_utils_convert_old_ip6_addr_array (const GValue *src_value, GValue *dst_value)
+{
+	GPtrArray *src_outer_array;
+	GPtrArray *dst_outer_array;
+	guint i;
+
+	g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
+
+	src_outer_array = (GPtrArray *) g_value_get_boxed (src_value);
+	dst_outer_array = g_ptr_array_new ();
+
+	for (i = 0; src_outer_array && (i < src_outer_array->len); i++) {
+		GValueArray *src_addr_array;
+		GValueArray *dst_addr_array;
+		GValue element = {0, };
+		GValue *src_addr, *src_prefix;
+		GByteArray *ba;
+
+		src_addr_array = (GValueArray *) g_ptr_array_index (src_outer_array, i);
+
+		if (   (src_addr_array->n_values != 2)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (src_addr_array, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (src_addr_array, 1)) != G_TYPE_UINT)) {
+			g_warning ("%s: invalid old IPv6 address type", __func__);
+			return;
+		}
+
+		dst_addr_array = g_value_array_new (3);
+
+		src_addr = g_value_array_get_nth (src_addr_array, 0);
+		g_value_array_append (dst_addr_array, src_addr);
+		src_prefix = g_value_array_get_nth (src_addr_array, 1);
+		g_value_array_append (dst_addr_array, src_prefix);
+
+		/* Blank Gateway */
+		g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+		ba = g_byte_array_new ();
+		g_byte_array_append (ba, (guint8 *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16);
+		g_value_take_boxed (&element, ba);
+		g_value_array_append (dst_addr_array, &element);
+		g_value_unset (&element);
+
+		g_ptr_array_add (dst_outer_array, dst_addr_array);
+	}
+
+	g_value_take_boxed (dst_value, dst_outer_array);
+}
+
+void
+_nm_utils_register_value_transformations (void)
+{
+	static gboolean registered = FALSE;
+
+	if (G_UNLIKELY (!registered)) {
+		g_value_register_transform_func (G_TYPE_STRV, 
+		                                 DBUS_TYPE_G_LIST_OF_STRING,
+		                                 nm_utils_convert_strv_to_slist);
+		g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_strv_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_UINT_ARRAY,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_uint_array_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_ip4_addr_route_struct_array_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_VARIANT,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_gvalue_hash_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_STRING,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_string_hash_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_UCHAR_ARRAY,
+		                                 G_TYPE_STRING,
+		                                 nm_utils_convert_byte_array_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_ip6_dns_array_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_ip6_addr_struct_array_to_string);
+		g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
+		                                 G_TYPE_STRING, 
+		                                 nm_utils_convert_ip6_route_struct_array_to_string);
+		g_value_register_transform_func (OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+		                                 DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+		                                 nm_utils_convert_old_ip6_addr_array);
+		registered = TRUE;
+	}
+}
+
+static gboolean
+device_supports_ap_ciphers (guint32 dev_caps,
+                            guint32 ap_flags,
+                            gboolean static_wep)
+{
+	gboolean have_pair = FALSE;
+	gboolean have_group = FALSE;
+	/* Device needs to support at least one pairwise and one group cipher */
+
+	/* Pairwise */
+	if (static_wep) {
+		/* Static WEP only uses group ciphers */
+		have_pair = TRUE;
+	} else {
+		if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40)
+			if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP40)
+				have_pair = TRUE;
+		if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104)
+			if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP104)
+				have_pair = TRUE;
+		if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)
+			if (ap_flags & NM_802_11_AP_SEC_PAIR_TKIP)
+				have_pair = TRUE;
+		if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)
+			if (ap_flags & NM_802_11_AP_SEC_PAIR_CCMP)
+				have_pair = TRUE;
+	}
+
+	/* Group */
+	if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40)
+		if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP40)
+			have_group = TRUE;
+	if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104)
+		if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP104)
+			have_group = TRUE;
+	if (!static_wep) {
+		if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)
+			if (ap_flags & NM_802_11_AP_SEC_GROUP_TKIP)
+				have_group = TRUE;
+		if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)
+			if (ap_flags & NM_802_11_AP_SEC_GROUP_CCMP)
+				have_group = TRUE;
+	}
+
+	return (have_pair && have_group);
+}
+
+/**
+ * nm_utils_security_valid:
+ * @type: the security type to check AP flags and device capabilties against,
+ * e.g. #NMU_SEC_STATIC_WEP
+ * @wifi_caps: bitfield of the capabilities of the specific WiFi device, e.g.
+ * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40
+ * @have_ap: whether the @ap_flags, @ap_wpa, and @ap_rsn arguments are valid
+ * @adhoc: whether the capabilities being tested are from an Ad-Hoc AP (IBSS)
+ * @ap_flags: bitfield of AP capabilities, e.g. #NM_802_11_AP_FLAGS_PRIVACY
+ * @ap_wpa: bitfield of AP capabilties derived from the AP's WPA beacon,
+ * e.g. (#NM_802_11_AP_SEC_PAIR_TKIP | #NM_802_11_AP_SEC_KEY_MGMT_PSK)
+ * @ap_rsn: bitfield of AP capabilties derived from the AP's RSN/WPA2 beacon,
+ * e.g. (#NM_802_11_AP_SEC_PAIR_CCMP | #NM_802_11_AP_SEC_PAIR_TKIP)
+ *
+ * Given a set of device capabilities, and a desired security type to check
+ * against, determines whether the combination of device, desired security
+ * type, and AP capabilities intersect.
+ *
+ * Returns: TRUE if the device capabilities and AP capabilties intersect and are
+ * compatible with the desired @type, FALSE if they are not
+ **/
+gboolean
+nm_utils_security_valid (NMUtilsSecurityType type,
+                         guint32 wifi_caps,
+                         gboolean have_ap,
+                         gboolean adhoc,
+                         guint32 ap_flags,
+                         guint32 ap_wpa,
+                         guint32 ap_rsn)
+{
+	gboolean good = TRUE;
+
+	if (!have_ap) {
+		if (type == NMU_SEC_NONE)
+			return TRUE;
+		if (   (type == NMU_SEC_STATIC_WEP)
+		    || ((type == NMU_SEC_DYNAMIC_WEP) && !adhoc)
+		    || ((type == NMU_SEC_LEAP) && !adhoc)) {
+			if (wifi_caps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104))
+				return TRUE;
+		}
+	}
+
+	switch (type) {
+	case NMU_SEC_NONE:
+		g_assert (have_ap);
+		if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+			return FALSE;
+		if (ap_wpa || ap_rsn)
+			return FALSE;
+		break;
+	case NMU_SEC_LEAP: /* require PRIVACY bit for LEAP? */
+		if (adhoc)
+			return FALSE;
+		/* Fall through */
+	case NMU_SEC_STATIC_WEP:
+		g_assert (have_ap);
+		if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY))
+			return FALSE;
+		if (ap_wpa || ap_rsn) {
+			if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, TRUE))
+				if (!device_supports_ap_ciphers (wifi_caps, ap_rsn, TRUE))
+					return FALSE;
+		}
+		break;
+	case NMU_SEC_DYNAMIC_WEP:
+		if (adhoc)
+			return FALSE;
+		g_assert (have_ap);
+		if (ap_rsn || !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY))
+			return FALSE;
+		/* Some APs broadcast minimal WPA-enabled beacons that must be handled */
+		if (ap_wpa) {
+			if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+				return FALSE;
+			if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, FALSE))
+				return FALSE;
+		}
+		break;
+	case NMU_SEC_WPA_PSK:
+		if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA))
+			return FALSE;
+		if (have_ap) {
+			/* Ad-Hoc WPA APs won't necessarily have the PSK flag set */
+			if ((ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK) || adhoc) {
+				if (   (ap_wpa & NM_802_11_AP_SEC_PAIR_TKIP)
+				    && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP))
+					return TRUE;
+				if (   (ap_wpa & NM_802_11_AP_SEC_PAIR_CCMP)
+				    && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+					return TRUE;
+			}
+			return FALSE;
+		}
+		break;
+	case NMU_SEC_WPA2_PSK:
+		if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN))
+			return FALSE;
+		if (have_ap) {
+			/* Ad-Hoc WPA APs won't necessarily have the PSK flag set */
+			if ((ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK) || adhoc) {
+				if (   (ap_rsn & NM_802_11_AP_SEC_PAIR_TKIP)
+				    && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP))
+					return TRUE;
+				if (   (ap_rsn & NM_802_11_AP_SEC_PAIR_CCMP)
+				    && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+					return TRUE;
+			}
+			return FALSE;
+		}
+		break;
+	case NMU_SEC_WPA_ENTERPRISE:
+		if (adhoc)
+			return FALSE;
+		if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA))
+			return FALSE;
+		if (have_ap) {
+			if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+				return FALSE;
+			/* Ensure at least one WPA cipher is supported */
+			if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, FALSE))
+				return FALSE;
+		}
+		break;
+	case NMU_SEC_WPA2_ENTERPRISE:
+		if (adhoc)
+			return FALSE;
+		if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN))
+			return FALSE;
+		if (have_ap) {
+			if (!(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+				return FALSE;
+			/* Ensure at least one WPA cipher is supported */
+			if (!device_supports_ap_ciphers (wifi_caps, ap_rsn, FALSE))
+				return FALSE;
+		}
+		break;
+	default:
+		good = FALSE;
+		break;
+	}
+
+	return good;
+}
+
+/**
+ * nm_utils_ip4_addresses_from_gvalue:
+ * @value: gvalue containing a GPtrArray of GArrays of guint32s
+ *
+ * Utility function to convert a #GPtrArray of #GArrays of guint32s representing
+ * a list of NetworkManager IPv4 addresses (which is a tuple of address, gateway,
+ * and prefix) into a GSList of #NMIP4Address objects.  The specific format of
+ * this serialization is not guaranteed to be stable and the #GArray may be
+ * extended in the future.
+ *
+ * Returns: a newly allocated #GSList of #NMIP4Address objects
+ **/
+GSList *
+nm_utils_ip4_addresses_from_gvalue (const GValue *value)
+{
+	GPtrArray *addresses;
+	int i;
+	GSList *list = NULL;
+
+	addresses = (GPtrArray *) g_value_get_boxed (value);
+	for (i = 0; addresses && (i < addresses->len); i++) {
+		GArray *array = (GArray *) g_ptr_array_index (addresses, i);
+		NMIP4Address *addr;
+
+		if (array->len < 3) {
+			nm_warning ("Ignoring invalid IP4 address");
+			continue;
+		}
+		
+		addr = nm_ip4_address_new ();
+		nm_ip4_address_set_address (addr, g_array_index (array, guint32, 0));
+		nm_ip4_address_set_prefix (addr, g_array_index (array, guint32, 1));
+		nm_ip4_address_set_gateway (addr, g_array_index (array, guint32, 2));
+		list = g_slist_prepend (list, addr);
+	}
+
+	return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip4_addresses_to_gvalue:
+ * @list: a list of #NMIP4Address objects
+ * @value: a pointer to a #GValue into which to place the converted addresses,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP4Address objects into a
+ * GPtrArray of GArrays of guint32s representing a list of NetworkManager IPv4
+ * addresses (which is a tuple of address, gateway, and prefix).   The specific
+ * format of this serialization is not guaranteed to be stable and may be
+ * extended in the future.
+ **/
+void
+nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value)
+{
+	GPtrArray *addresses;
+	GSList *iter;
+
+	addresses = g_ptr_array_new ();
+
+	for (iter = list; iter; iter = iter->next) {
+		NMIP4Address *addr = (NMIP4Address *) iter->data;
+		GArray *array;
+		guint32 tmp;
+
+		array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+
+		tmp = nm_ip4_address_get_address (addr);
+		g_array_append_val (array, tmp);
+
+		tmp = nm_ip4_address_get_prefix (addr);
+		g_array_append_val (array, tmp);
+
+		tmp = nm_ip4_address_get_gateway (addr);
+		g_array_append_val (array, tmp);
+
+		g_ptr_array_add (addresses, array);
+	}
+
+	g_value_take_boxed (value, addresses);
+}
+
+/**
+ * nm_utils_ip4_routes_from_gvalue:
+ * @value: gvalue containing a GPtrArray of GArrays of guint32s
+ *
+ * Utility function to convert a GPtrArray of GArrays of guint32s representing
+ * a list of NetworkManager IPv4 routes (which is a tuple of route, next hop,
+ * prefix, and metric) into a GSList of #NMIP4Route objects.  The specific
+ * format of this serialization is not guaranteed to be stable and may be
+ * extended in the future.
+ *
+ * Returns: a newly allocated #GSList of #NMIP4Route objects
+ **/
+GSList *
+nm_utils_ip4_routes_from_gvalue (const GValue *value)
+{
+	GPtrArray *routes;
+	int i;
+	GSList *list = NULL;
+
+	routes = (GPtrArray *) g_value_get_boxed (value);
+	for (i = 0; routes && (i < routes->len); i++) {
+		GArray *array = (GArray *) g_ptr_array_index (routes, i);
+		NMIP4Route *route;
+
+		if (array->len < 4) {
+			nm_warning ("Ignoring invalid IP4 route");
+			continue;
+		}
+		
+		route = nm_ip4_route_new ();
+		nm_ip4_route_set_dest (route, g_array_index (array, guint32, 0));
+		nm_ip4_route_set_prefix (route, g_array_index (array, guint32, 1));
+		nm_ip4_route_set_next_hop (route, g_array_index (array, guint32, 2));
+		nm_ip4_route_set_metric (route, g_array_index (array, guint32, 3));
+		list = g_slist_prepend (list, route);
+	}
+
+	return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip4_routes_to_gvalue:
+ * @list: a list of #NMIP4Route objects
+ * @value: a pointer to a #GValue into which to place the converted routes,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP4Route objects into a
+ * GPtrArray of GArrays of guint32s representing a list of NetworkManager IPv4
+ * routes (which is a tuple of route, next hop, prefix, and metric).   The
+ * specific format of this serialization is not guaranteed to be stable and may
+ * be extended in the future.
+ **/
+void
+nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value)
+{
+	GPtrArray *routes;
+	GSList *iter;
+
+	routes = g_ptr_array_new ();
+
+	for (iter = list; iter; iter = iter->next) {
+		NMIP4Route *route = (NMIP4Route *) iter->data;
+		GArray *array;
+		guint32 tmp;
+
+		array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+
+		tmp = nm_ip4_route_get_dest (route);
+		g_array_append_val (array, tmp);
+
+		tmp = nm_ip4_route_get_prefix (route);
+		g_array_append_val (array, tmp);
+
+		tmp = nm_ip4_route_get_next_hop (route);
+		g_array_append_val (array, tmp);
+
+		tmp = nm_ip4_route_get_metric (route);
+		g_array_append_val (array, tmp);
+
+		g_ptr_array_add (routes, array);
+	}
+
+	g_value_take_boxed (value, routes);
+}
+
+/**
+ * nm_utils_ip4_netmask_to_prefix:
+ * @netmask: an IPv4 netmask in network byte order
+ *
+ * Returns: the CIDR prefix represented by the netmask
+ **/
+guint32
+nm_utils_ip4_netmask_to_prefix (guint32 netmask)
+{
+	guchar *p, *end;
+	guint32 prefix = 0;
+
+	p = (guchar *) &netmask;
+	end = p + sizeof (guint32);
+
+	while ((*p == 0xFF) && p < end) {
+		prefix += 8;
+		p++;
+	}
+
+	if (p < end) {
+		guchar v = *p;
+
+		while (v) {
+			prefix++;
+			v <<= 1;
+		}
+	}
+
+	return prefix;
+}
+
+/**
+ * nm_utils_ip4_prefix_to_netmask:
+ * @prefix: a CIDR prefix
+ *
+ * Returns: the netmask represented by the prefix
+ **/
+guint32
+nm_utils_ip4_prefix_to_netmask (guint32 prefix)
+{
+	guint32 msk = 0x80000000;
+	guint32 netmask = 0;
+
+	while (prefix > 0) {
+		netmask |= msk;
+		msk >>= 1;
+		prefix--;
+	}
+
+	return (guint32) htonl (netmask);
+}
+
+
+/**
+ * nm_utils_ip4_get_default_prefix:
+ * @ip: an IPv4 address (in network byte order)
+ *
+ * When the Internet was originally set up, various ranges of IP addresses were
+ * segmented into three network classes: A, B, and C.  This function will return
+ * a prefix that is associated with the IP address specified defining where it
+ * falls in the predefined classes.
+ *
+ * Returns: the default class prefix for the given IP
+ **/
+/* The function is originally from ipcalc.c of Red Hat's initscripts. */
+guint32
+nm_utils_ip4_get_default_prefix (guint32 ip)
+{
+	if (((ntohl (ip) & 0xFF000000) >> 24) <= 127)
+		return 8;  /* Class A - 255.0.0.0 */
+	else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191)
+		return 16;  /* Class B - 255.255.0.0 */
+
+	return 24;  /* Class C - 255.255.255.0 */
+}
+
+/**
+ * nm_utils_ip6_addresses_from_gvalue:
+ * @value: gvalue containing a GPtrArray of GValueArrays of (GArray of guchars) and guint32
+ *
+ * Utility function to convert a #GPtrArray of #GValueArrays of (#GArray of guchars) and guint32
+ * representing a list of NetworkManager IPv6 addresses (which is a tuple of address,
+ * prefix, and gateway), into a GSList of #NMIP6Address objects.  The specific format of
+ * this serialization is not guaranteed to be stable and the #GValueArray may be
+ * extended in the future.
+ *
+ * Returns: a newly allocated #GSList of #NMIP6Address objects
+ **/
+GSList *
+nm_utils_ip6_addresses_from_gvalue (const GValue *value)
+{
+	GPtrArray *addresses;
+	int i;
+	GSList *list = NULL;
+
+	addresses = (GPtrArray *) g_value_get_boxed (value);
+
+	for (i = 0; addresses && (i < addresses->len); i++) {
+		GValueArray *elements = (GValueArray *) g_ptr_array_index (addresses, i);
+		GValue *tmp;
+		GByteArray *ba_addr;
+		GByteArray *ba_gw = NULL;
+		NMIP6Address *addr;
+		guint32 prefix;
+
+		if (elements->n_values < 2 || elements->n_values > 3) {
+			nm_warning ("%s: ignoring invalid IP6 address structure", __func__);
+			continue;
+		}
+
+		if (   (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)) {
+			nm_warning ("%s: ignoring invalid IP6 address structure", __func__);
+			continue;
+		}
+
+		/* Check optional 3rd element (gateway) */
+		if (   elements->n_values == 3
+		    && (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)) {
+			nm_warning ("%s: ignoring invalid IP6 address structure", __func__);
+			continue;
+		}
+
+		tmp = g_value_array_get_nth (elements, 0);
+		ba_addr = g_value_get_boxed (tmp);
+		if (ba_addr->len != 16) {
+			nm_warning ("%s: ignoring invalid IP6 address of length %d",
+			            __func__, ba_addr->len);
+			continue;
+		}
+
+		tmp = g_value_array_get_nth (elements, 1);
+		prefix = g_value_get_uint (tmp);
+		if (prefix > 128) {
+			nm_warning ("%s: ignoring invalid IP6 prefix %d",
+			            __func__, prefix);
+			continue;
+		}
+
+		if (elements->n_values == 3) {
+			tmp = g_value_array_get_nth (elements, 2);
+			ba_gw = g_value_get_boxed (tmp);
+			if (ba_gw->len != 16) {
+				nm_warning ("%s: ignoring invalid IP6 gateway address of length %d",
+				            __func__, ba_gw->len);
+				continue;
+			}
+		}
+
+		addr = nm_ip6_address_new ();
+		nm_ip6_address_set_prefix (addr, prefix);
+		nm_ip6_address_set_address (addr, (const struct in6_addr *) ba_addr->data);
+		if (ba_gw)
+			nm_ip6_address_set_gateway (addr, (const struct in6_addr *) ba_gw->data);
+
+		list = g_slist_prepend (list, addr);
+	}
+
+	return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip6_addresses_to_gvalue:
+ * @list: a list of #NMIP6Address objects
+ * @value: a pointer to a #GValue into which to place the converted addresses,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP6Address objects into a
+ * GPtrArray of GValueArrays representing a list of NetworkManager IPv6 addresses
+ * (which is a tuple of address, prefix, and gateway). The specific format of
+ * this serialization is not guaranteed to be stable and may be extended in the
+ * future.
+ **/
+void
+nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value)
+{
+	GPtrArray *addresses;
+	GSList *iter;
+
+	addresses = g_ptr_array_new ();
+
+	for (iter = list; iter; iter = iter->next) {
+		NMIP6Address *addr = (NMIP6Address *) iter->data;
+		GValueArray *array;
+		GValue element = {0, };
+		GByteArray *ba;
+
+		array = g_value_array_new (3);
+
+		/* IP address */
+		g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+		ba = g_byte_array_new ();
+		g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_address (addr), 16);
+		g_value_take_boxed (&element, ba);
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		/* Prefix */
+		g_value_init (&element, G_TYPE_UINT);
+		g_value_set_uint (&element, nm_ip6_address_get_prefix (addr));
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		/* Gateway */
+		g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+		ba = g_byte_array_new ();
+		g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_gateway (addr), 16);
+		g_value_take_boxed (&element, ba);
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		g_ptr_array_add (addresses, array);
+	}
+
+	g_value_take_boxed (value, addresses);
+}
+
+/**
+ * nm_utils_ip6_routes_from_gvalue:
+ * @value: gvalue containing a GPtrArray of GValueArrays of (GArray or guchars), guint32,
+ * (GArray of guchars), and guint32
+ *
+ * Utility function GPtrArray of GValueArrays of (GArray or guchars), guint32,
+ * (GArray of guchars), and guint32 representing a list of NetworkManager IPv6
+ * routes (which is a tuple of destination, prefix, next hop, and metric)
+ * into a GSList of #NMIP6Route objects.  The specific format of this serialization
+ * is not guaranteed to be stable and may be extended in the future.
+ *
+ * Returns: a newly allocated #GSList of #NMIP6Route objects
+ **/
+GSList *
+nm_utils_ip6_routes_from_gvalue (const GValue *value)
+{
+	GPtrArray *routes;
+	int i;
+	GSList *list = NULL;
+
+	routes = (GPtrArray *) g_value_get_boxed (value);
+	for (i = 0; routes && (i < routes->len); i++) {
+		GValueArray *route_values = (GValueArray *) g_ptr_array_index (routes, i);
+		GByteArray *dest, *next_hop;
+		guint prefix, metric;
+		NMIP6Route *route;
+
+		if (   (route_values->n_values != 4)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (route_values, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
+			|| (G_VALUE_TYPE (g_value_array_get_nth (route_values, 1)) != G_TYPE_UINT)
+		    || (G_VALUE_TYPE (g_value_array_get_nth (route_values, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)
+			|| (G_VALUE_TYPE (g_value_array_get_nth (route_values, 3)) != G_TYPE_UINT)) {
+			nm_warning ("Ignoring invalid IP6 route");
+			continue;
+		}
+
+		dest = g_value_get_boxed (g_value_array_get_nth (route_values, 0));
+		if (dest->len != 16) {
+			nm_warning ("%s: ignoring invalid IP6 dest address of length %d",
+			            __func__, dest->len);
+			continue;
+		}
+
+		prefix = g_value_get_uint (g_value_array_get_nth (route_values, 1));
+
+		next_hop = g_value_get_boxed (g_value_array_get_nth (route_values, 2));
+		if (next_hop->len != 16) {
+			nm_warning ("%s: ignoring invalid IP6 next_hop address of length %d",
+			            __func__, next_hop->len);
+			continue;
+		}
+
+		metric = g_value_get_uint (g_value_array_get_nth (route_values, 3));
+
+		route = nm_ip6_route_new ();
+		nm_ip6_route_set_dest (route, (struct in6_addr *)dest->data);
+		nm_ip6_route_set_prefix (route, prefix);
+		nm_ip6_route_set_next_hop (route, (struct in6_addr *)next_hop->data);
+		nm_ip6_route_set_metric (route, metric);
+		list = g_slist_prepend (list, route);
+	}
+
+	return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip6_routes_to_gvalue:
+ * @list: a list of #NMIP6Route objects
+ * @value: a pointer to a #GValue into which to place the converted routes,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP6Route objects into a GPtrArray of
+ * GValueArrays of (GArray or guchars), guint32, (GArray of guchars), and guint32
+ * representing a list of NetworkManager IPv6 routes (which is a tuple of destination,
+ * prefix, next hop, and metric).  The specific format of this serialization is not 
+ * guaranteed to be stable and may be extended in the future.
+ **/
+void
+nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value)
+{
+	GPtrArray *routes;
+	GSList *iter;
+
+	routes = g_ptr_array_new ();
+
+	for (iter = list; iter; iter = iter->next) {
+		NMIP6Route *route = (NMIP6Route *) iter->data;
+		GValueArray *array;
+		const struct in6_addr *addr;
+		GByteArray *ba;
+		GValue element = {0, };
+
+		array = g_value_array_new (4);
+
+		g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+		addr = nm_ip6_route_get_dest (route);
+		ba = g_byte_array_new ();
+		g_byte_array_append (ba, (guchar *)addr, sizeof (*addr));
+		g_value_take_boxed (&element, ba);
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		g_value_init (&element, G_TYPE_UINT);
+		g_value_set_uint (&element, nm_ip6_route_get_prefix (route));
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+		addr = nm_ip6_route_get_next_hop (route);
+		ba = g_byte_array_new ();
+		g_byte_array_append (ba, (guchar *)addr, sizeof (*addr));
+		g_value_take_boxed (&element, ba);
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		g_value_init (&element, G_TYPE_UINT);
+		g_value_set_uint (&element, nm_ip6_route_get_metric (route));
+		g_value_array_append (array, &element);
+		g_value_unset (&element);
+
+		g_ptr_array_add (routes, array);
+	}
+
+	g_value_take_boxed (value, routes);
+}
+
+GSList *
+nm_utils_ip6_dns_from_gvalue (const GValue *value)
+{
+	GPtrArray *dns;
+	int i;
+	GSList *list = NULL;
+
+	dns = (GPtrArray *) g_value_get_boxed (value);
+	for (i = 0; dns && (i < dns->len); i++) {
+		GByteArray *bytearray = (GByteArray *) g_ptr_array_index (dns, i);
+		struct in6_addr *addr;
+
+		if (bytearray->len != 16) {
+			nm_warning ("%s: ignoring invalid IP6 address of length %d",
+			            __func__, bytearray->len);
+			continue;
+		}
+
+		addr = g_malloc0 (sizeof (struct in6_addr));
+		memcpy (addr->s6_addr, bytearray->data, bytearray->len);
+		list = g_slist_prepend (list, addr);
+	}
+
+	return g_slist_reverse (list);
+}
+
+void
+nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value)
+{
+	GPtrArray *dns;
+	GSList *iter;
+
+	dns = g_ptr_array_new ();
+
+	for (iter = list; iter; iter = iter->next) {
+		struct in6_addr *addr = (struct in6_addr *) iter->data;
+		GByteArray *bytearray;
+
+		bytearray = g_byte_array_sized_new (16);
+		g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16);
+		g_ptr_array_add (dns, bytearray);
+	}
+
+	g_value_take_boxed (value, dns);
+}
+
+/**
+ * nm_utils_uuid_generate:
+ *
+ * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection
+ * object's #NMSettingConnection:id: property.  Should be freed with g_free()
+ **/
+char *
+nm_utils_uuid_generate (void)
+{
+	uuid_t uuid;
+	char *buf;
+
+	buf = g_malloc0 (37);
+	uuid_generate_random (uuid);
+	uuid_unparse_lower (uuid, &buf[0]);
+	return buf;
+}
+
+/**
+ * nm_utils_uuid_generate_from_string:
+ * @s: a string to use as the seed for the UUID
+ *
+ * For a given @s, this function will always return the same UUID.
+ *
+ * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection
+ * object's #NMSettingConnection:id: property
+ **/
+char *
+nm_utils_uuid_generate_from_string (const char *s)
+{
+	GError *error = NULL;
+	uuid_t *uuid;
+	char *buf = NULL;
+
+	if (!nm_utils_init (&error)) {
+		nm_warning ("error initializing crypto: (%d) %s",
+		            error ? error->code : 0,
+		            error ? error->message : "unknown");
+		if (error)
+			g_error_free (error);
+		return NULL;
+	}
+
+	uuid = g_malloc0 (sizeof (*uuid));
+	if (!crypto_md5_hash (NULL, 0, s, strlen (s), (char *) uuid, sizeof (*uuid), &error)) {
+		nm_warning ("error generating UUID: (%d) %s",
+		            error ? error->code : 0,
+		            error ? error->message : "unknown");
+		if (error)
+			g_error_free (error);
+		goto out;
+	}
+
+	buf = g_malloc0 (37);
+	uuid_unparse_lower (*uuid, &buf[0]);
+
+out:
+	g_free (uuid);
+	return buf;
+}
+
+static char *
+make_key (const char *salt,
+          const gsize salt_len,
+          const char *password,
+          gsize *out_len,
+          GError **error)
+{
+	char *key;
+	guint32 digest_len = 24; /* DES-EDE3-CBC */
+
+	g_return_val_if_fail (salt != NULL, NULL);
+	g_return_val_if_fail (salt_len >= 8, NULL);
+	g_return_val_if_fail (password != NULL, NULL);
+	g_return_val_if_fail (out_len != NULL, NULL);
+
+	key = g_malloc0 (digest_len + 1);
+	if (!key) {
+		g_set_error (error,
+		             NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Not enough memory to make encryption key."));
+		return NULL;
+	}
+
+	if (!crypto_md5_hash (salt, salt_len, password, strlen (password), key, digest_len, error)) {
+		*out_len = 0;
+		memset (key, 0, digest_len);
+		g_free (key);
+		key = NULL;
+	} else
+		*out_len = digest_len;
+
+	return key;
+}
+
+/*
+ * utils_bin2hexstr
+ *
+ * Convert a byte-array into a hexadecimal string.
+ *
+ * Code originally by Alex Larsson <alexl@redhat.com> and
+ *  copyright Red Hat, Inc. under terms of the LGPL.
+ *
+ */
+static char *
+utils_bin2hexstr (const char *bytes, int len, int final_len)
+{
+	static char hex_digits[] = "0123456789abcdef";
+	char *result;
+	int i;
+	gsize buflen = (len * 2) + 1;
+
+	g_return_val_if_fail (bytes != NULL, NULL);
+	g_return_val_if_fail (len > 0, NULL);
+	g_return_val_if_fail (len < 4096, NULL);   /* Arbitrary limit */
+	if (final_len > -1)
+		g_return_val_if_fail (final_len < buflen, NULL);
+
+	result = g_malloc0 (buflen);
+	for (i = 0; i < len; i++)
+	{
+		result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf];
+		result[2*i+1] = hex_digits[bytes[i] & 0xf];
+	}
+	/* Cut converted key off at the correct length for this cipher type */
+	if (final_len > -1)
+		result[final_len] = '\0';
+	else
+		result[buflen - 1] = '\0';
+
+	return result;
+}
+
+/**
+ * nm_utils_rsa_key_encrypt:
+ * @data: RSA private key data to be encrypted
+ * @in_password: existing password to use, if any
+ * @out_password: if @in_password was NULL, a random password will be generated
+ *  and returned in this argument
+ * @error: detailed error information on return, if an error occurred
+ *
+ * Encrypts the given RSA private key data with the given password (or generates
+ * a password if no password was given) and converts the data to PEM format
+ * suitable for writing to a file.
+ *
+ * Returns: on success, PEM-formatted data suitable for writing to a PEM-formatted
+ * certificate/private key file.
+ **/
+GByteArray *
+nm_utils_rsa_key_encrypt (const GByteArray *data,
+                          const char *in_password,
+                          char **out_password,
+                          GError **error)
+{
+	char salt[8];
+	char *key = NULL, *enc = NULL, *pw_buf[32];
+	gsize key_len = 0, enc_len = 0;
+	GString *pem = NULL;
+	char *tmp, *tmp_password = NULL;
+	int left;
+	const char *p;
+	GByteArray *ret = NULL;
+
+	g_return_val_if_fail (data != NULL, NULL);
+	g_return_val_if_fail (data->len > 0, NULL);
+	if (out_password)
+		g_return_val_if_fail (*out_password == NULL, NULL);
+
+	/* Make the password if needed */
+	if (!in_password) {
+		if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
+			return NULL;
+		in_password = tmp_password = utils_bin2hexstr ((const char *) pw_buf, sizeof (pw_buf), -1);
+	}
+
+	if (!crypto_randomize (salt, sizeof (salt), error))
+		goto out;
+
+	key = make_key (&salt[0], sizeof (salt), in_password, &key_len, error);
+	if (!key)
+		goto out;
+
+	enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, salt, sizeof (salt), key, key_len, &enc_len, error);
+	if (!enc)
+		goto out;
+
+	pem = g_string_sized_new (enc_len * 2 + 100);
+	if (!pem) {
+		g_set_error_literal (error, NM_CRYPTO_ERROR,
+		                     NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		                     _("Could not allocate memory for PEM file creation."));
+		goto out;
+	}
+
+	g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n");
+	g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
+
+	/* Convert the salt to a hex string */
+	tmp = utils_bin2hexstr ((const char *) salt, sizeof (salt), 16);
+	if (!tmp) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Could not allocate memory for writing IV to PEM file."));
+		goto out;
+	}
+
+	g_string_append_printf (pem, "DEK-Info: DES-EDE3-CBC,%s\n\n", tmp);
+	g_free (tmp);
+
+	/* Convert the encrypted key to a base64 string */
+	p = tmp = g_base64_encode ((const guchar *) enc, enc_len);
+	if (!tmp) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Could not allocate memory for writing encrypted key to PEM file."));
+		goto out;
+	}
+
+	left = strlen (tmp);
+	while (left > 0) {
+		g_string_append_len (pem, p, (left < 64) ? left : 64);
+		g_string_append_c (pem, '\n');
+		left -= 64;
+		p += 64;
+	}
+	g_free (tmp);
+
+	g_string_append (pem, "-----END RSA PRIVATE KEY-----\n");
+
+	ret = g_byte_array_sized_new (pem->len);
+	if (!ret) {
+		g_set_error (error, NM_CRYPTO_ERROR,
+		             NM_CRYPTO_ERR_OUT_OF_MEMORY,
+		             _("Could not allocate memory for PEM file data."));
+		goto out;
+	}
+	g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len);
+	if (tmp_password && out_password)
+		*out_password = g_strdup (tmp_password);
+
+out:
+	if (key) {
+		memset (key, 0, key_len);
+		g_free (key);
+	}
+	if (enc) {
+		memset (enc, 0, enc_len);
+		g_free (enc);
+	}
+	if (pem)
+		g_string_free (pem, TRUE);
+
+	if (tmp_password) {
+		memset (tmp_password, 0, strlen (tmp_password));
+		g_free (tmp_password);
+	}
+
+	return ret;
+}
+
diff --git a/libnm-util/nm-utils.h b/libnm-util/nm-utils.h
new file mode 100644
index 00000000..68501d53
--- /dev/null
+++ b/libnm-util/nm-utils.h
@@ -0,0 +1,218 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * Ray Strode <rstrode@redhat.com>
+ * Dan Williams <dcbw@redhat.com>
+ * Tambet Ingo <tambet@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2005 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_UTILS_H
+#define NM_UTILS_H
+
+#include <glib.h>
+#include <execinfo.h>
+
+#include "nm-connection.h"
+
+G_BEGIN_DECLS
+
+/*********************************************************/
+/* The API defined here is _NOT_ guaranteed in any way!! */
+/*********************************************************/
+
+/**
+ * nm_print_backtrace:
+ *
+ * Prints a backtrace of the calling process to the logging location.
+ */
+#define nm_print_backtrace()						\
+G_STMT_START								\
+{									\
+	void *_call_stack[512];						\
+	int  _call_stack_size;						\
+	char **_symbols;						\
+	_call_stack_size = backtrace (_call_stack,			\
+				      G_N_ELEMENTS (_call_stack));	\
+	_symbols = backtrace_symbols (_call_stack, _call_stack_size);	\
+	if (_symbols != NULL)						\
+	{								\
+		int _i;							\
+		_i = 0;							\
+		g_critical ("traceback:\n");				\
+		while (_i < _call_stack_size)				\
+		{							\
+			g_critical ("\t%s\n", _symbols[_i]);		\
+			_i++;						\
+		}							\
+		free (_symbols);					\
+	}								\
+}									\
+G_STMT_END
+
+/**
+ * nm_get_timestamp:
+ * @timestamp: location in which to place the current timestamp
+ *
+ * For debugging only.
+ */
+#define nm_get_timestamp(timestamp)					\
+G_STMT_START								\
+{									\
+	GTimeVal _tv;							\
+	g_get_current_time (&_tv);					\
+	*timestamp = (_tv.tv_sec * (1.0 * G_USEC_PER_SEC) +		\
+		      _tv.tv_usec) / G_USEC_PER_SEC;			\
+}									\
+G_STMT_END
+
+#define nm_info(fmt, args...)						\
+G_STMT_START								\
+{									\
+	g_message ("<info>  " fmt "\n", ##args);			\
+} G_STMT_END
+
+#define nm_info_str(fmt_str, args...)						\
+G_STMT_START								\
+{									\
+	g_message ("<info>  %s\n", fmt_str, ##args);			\
+} G_STMT_END
+
+#define nm_debug(fmt, args...)						\
+G_STMT_START								\
+{									\
+	gdouble _timestamp;						\
+	nm_get_timestamp (&_timestamp);					\
+	g_debug ("<debug> [%f] %s(): " fmt "\n", _timestamp,	\
+		 G_STRFUNC, ##args);				\
+} G_STMT_END
+
+#define nm_debug_str(fmt_str, args...)						\
+G_STMT_START								\
+{									\
+	gdouble _timestamp;						\
+	nm_get_timestamp (&_timestamp);					\
+	g_debug ("<debug> [%f] %s(): %s\n", _timestamp,	\
+		 G_STRFUNC, fmt_str, ##args);				\
+} G_STMT_END
+
+#define nm_warning(fmt, args...)					\
+G_STMT_START								\
+{									\
+	g_warning ("<WARN>  %s(): " fmt "\n", 			\
+		   G_STRFUNC, ##args);			\
+} G_STMT_END
+
+#define nm_warning_str(fmt_str, args...)					\
+G_STMT_START								\
+{									\
+	g_warning ("<WARN>  %s(): %s\n", 			\
+		   G_STRFUNC, fmt_str, ##args);			\
+} G_STMT_END
+
+#define nm_error(fmt, args...)						\
+G_STMT_START								\
+{									\
+	gdouble _timestamp;						\
+	nm_get_timestamp (&_timestamp);					\
+	g_critical ("<ERROR>\t[%f] %s (): " fmt "\n", _timestamp,	\
+		    G_STRFUNC, ##args);			\
+	nm_print_backtrace ();						\
+	G_BREAKPOINT ();						\
+} G_STMT_END
+
+#define nm_error_str(fmt_str, args...)						\
+G_STMT_START								\
+{									\
+	gdouble _timestamp;						\
+	nm_get_timestamp (&_timestamp);					\
+	g_critical ("<ERROR>\t[%f] %s (): %s\n", _timestamp,	\
+		    G_STRFUNC, fmt_str, ##args);			\
+	nm_print_backtrace ();						\
+	G_BREAKPOINT ();						\
+} G_STMT_END
+
+/* init, deinit nm_utils */
+gboolean nm_utils_init (GError **error);
+void     nm_utils_deinit (void);
+
+/* SSID helpers */
+gboolean    nm_utils_is_empty_ssid    (const guint8 * ssid, int len);
+const char *nm_utils_escape_ssid      (const guint8 *ssid, guint32 len);
+gboolean    nm_utils_same_ssid        (const GByteArray * ssid1,
+							    const GByteArray * ssid2,
+							    gboolean ignore_trailing_null);
+
+char *nm_utils_ssid_to_utf8 (const char *ssid, guint32 len);
+
+GHashTable *nm_utils_gvalue_hash_dup  (GHashTable *hash);
+
+void        nm_utils_slist_free       (GSList *list,
+							    GDestroyNotify elem_destroy_fn);
+
+typedef enum {
+	NMU_SEC_INVALID = 0,
+	NMU_SEC_NONE,
+	NMU_SEC_STATIC_WEP,
+	NMU_SEC_LEAP,
+	NMU_SEC_DYNAMIC_WEP,
+	NMU_SEC_WPA_PSK,
+	NMU_SEC_WPA_ENTERPRISE,
+	NMU_SEC_WPA2_PSK,
+	NMU_SEC_WPA2_ENTERPRISE
+} NMUtilsSecurityType;
+
+gboolean nm_utils_security_valid (NMUtilsSecurityType type,
+                                  guint32 wifi_caps,
+                                  gboolean have_ap,
+                                  gboolean adhoc,
+                                  guint32 ap_flags,
+                                  guint32 ap_wpa,
+                                  guint32 ap_rsn);
+
+GSList *nm_utils_ip4_addresses_from_gvalue (const GValue *value);
+void nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value);
+
+GSList *nm_utils_ip4_routes_from_gvalue (const GValue *value);
+void nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value);
+
+guint32 nm_utils_ip4_netmask_to_prefix (guint32 netmask);
+guint32 nm_utils_ip4_prefix_to_netmask (guint32 prefix);
+guint32 nm_utils_ip4_get_default_prefix (guint32 ip);
+
+GSList *nm_utils_ip6_addresses_from_gvalue (const GValue *value);
+void nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value);
+
+GSList *nm_utils_ip6_routes_from_gvalue (const GValue *value);
+void nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value);
+
+GSList *nm_utils_ip6_dns_from_gvalue (const GValue *value);
+void nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value);
+
+char *nm_utils_uuid_generate (void);
+char *nm_utils_uuid_generate_from_string (const char *s);
+
+GByteArray *nm_utils_rsa_key_encrypt (const GByteArray *data,
+                                      const char *in_password,
+                                      char **out_password,
+                                      GError **error);
+
+G_END_DECLS
+
+#endif /* NM_UTILS_H */
diff --git a/libnm-util/tests/Makefile.am b/libnm-util/tests/Makefile.am
new file mode 100644
index 00000000..378a7388
--- /dev/null
+++ b/libnm-util/tests/Makefile.am
@@ -0,0 +1,139 @@
+SUBDIRS=certs
+
+INCLUDES = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util
+
+noinst_PROGRAMS = \
+	test-settings-defaults \
+	test-crypto \
+	test-need-secrets \
+	test-general \
+	test-setting-8021x
+
+test_settings_defaults_SOURCES = \
+	test-settings-defaults.c
+
+test_settings_defaults_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_settings_defaults_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+test_crypto_SOURCES = \
+	test-crypto.c
+
+test_crypto_CPPFLAGS = \
+	$(GLIB_CFLAGS)
+
+test_crypto_LDADD = \
+	$(top_builddir)/libnm-util/libtest-crypto.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS)
+
+test_need_secrets_SOURCES = \
+	test-need-secrets.c
+
+test_need_secrets_CPPFLAGS = \
+	-DTEST_CERT_DIR=\"$(top_srcdir)/libnm-util/tests/certs/\" \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_need_secrets_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+test_general_SOURCES = \
+	test-general.c
+
+test_general_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_general_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+test_setting_8021x_SOURCES = \
+	test-setting-8021x.c
+
+test_setting_8021x_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_setting_8021x_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+if WITH_TESTS
+
+check-local: test-settings-defaults test-crypto test-need-secrets
+	$(abs_builddir)/test-settings-defaults
+	$(abs_builddir)/test-need-secrets
+	$(abs_builddir)/test-general
+
+# Private key and CA certificate in the same file (PEM)
+	$(abs_builddir)/test-setting-8021x \
+		$(top_srcdir)/libnm-util/tests/certs/test_key_and_cert.pem \
+		"test" \
+		$(top_srcdir)/libnm-util/tests/certs/test-key-only-decrypted.der
+
+# Private key by itself (PEM)
+	$(abs_builddir)/test-setting-8021x \
+		$(top_srcdir)/libnm-util/tests/certs/test-key-only.pem \
+		"test" \
+		$(top_srcdir)/libnm-util/tests/certs/test-key-only-decrypted.der
+
+# Private key and CA certificate in the same file (pkcs12)
+	$(abs_builddir)/test-setting-8021x \
+		$(top_srcdir)/libnm-util/tests/certs/test-cert.p12 \
+		"test"
+
+# Normal CA certificate
+	$(abs_builddir)/test-crypto --cert \
+		$(top_srcdir)/libnm-util/tests/certs/test_ca_cert.pem
+
+# Another CA certificate
+	$(abs_builddir)/test-crypto --cert \
+		$(top_srcdir)/libnm-util/tests/certs/test2_ca_cert.pem
+
+# CA certificate without an ending newline
+	$(abs_builddir)/test-crypto --cert \
+		$(top_srcdir)/libnm-util/tests/certs/ca-no-ending-newline.pem
+
+# Combined user cert and private key
+	$(abs_builddir)/test-crypto --cert \
+		$(top_srcdir)/libnm-util/tests/certs/test_key_and_cert.pem
+
+# Another combined user cert and private key
+	$(abs_builddir)/test-crypto --cert \
+		$(top_srcdir)/libnm-util/tests/certs/test2_key_and_cert.pem
+
+# Private key with 8 bytes of tail padding
+	$(abs_builddir)/test-crypto --key \
+		$(top_srcdir)/libnm-util/tests/certs/test_key_and_cert.pem \
+		"test"
+
+# Private key with 6 bytes of tail padding
+	$(abs_builddir)/test-crypto --key \
+		$(top_srcdir)/libnm-util/tests/certs/test2_key_and_cert.pem \
+		"12345testing"
+
+# PKCS#12 file
+	$(abs_builddir)/test-crypto --p12 \
+		$(top_srcdir)/libnm-util/tests/certs/test-cert.p12 \
+		"test"
+
+# Another PKCS#12 file
+	$(abs_builddir)/test-crypto --p12 \
+		$(top_srcdir)/libnm-util/tests/certs/test2-cert.p12 \
+		"12345testing"
+
+endif
+
diff --git a/libnm-util/tests/Makefile.in b/libnm-util/tests/Makefile.in
new file mode 100644
index 00000000..9654a565
--- /dev/null
+++ b/libnm-util/tests/Makefile.in
@@ -0,0 +1,935 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = test-settings-defaults$(EXEEXT) test-crypto$(EXEEXT) \
+	test-need-secrets$(EXEEXT) test-general$(EXEEXT) \
+	test-setting-8021x$(EXEEXT)
+subdir = libnm-util/tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_test_crypto_OBJECTS = test_crypto-test-crypto.$(OBJEXT)
+test_crypto_OBJECTS = $(am_test_crypto_OBJECTS)
+am__DEPENDENCIES_1 =
+test_crypto_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libtest-crypto.la \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1)
+am_test_general_OBJECTS = test_general-test-general.$(OBJEXT)
+test_general_OBJECTS = $(am_test_general_OBJECTS)
+test_general_DEPENDENCIES = $(top_builddir)/libnm-util/libnm-util.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_test_need_secrets_OBJECTS =  \
+	test_need_secrets-test-need-secrets.$(OBJEXT)
+test_need_secrets_OBJECTS = $(am_test_need_secrets_OBJECTS)
+test_need_secrets_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am_test_setting_8021x_OBJECTS =  \
+	test_setting_8021x-test-setting-8021x.$(OBJEXT)
+test_setting_8021x_OBJECTS = $(am_test_setting_8021x_OBJECTS)
+test_setting_8021x_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am_test_settings_defaults_OBJECTS =  \
+	test_settings_defaults-test-settings-defaults.$(OBJEXT)
+test_settings_defaults_OBJECTS = $(am_test_settings_defaults_OBJECTS)
+test_settings_defaults_DEPENDENCIES =  \
+	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+SOURCES = $(test_crypto_SOURCES) $(test_general_SOURCES) \
+	$(test_need_secrets_SOURCES) $(test_setting_8021x_SOURCES) \
+	$(test_settings_defaults_SOURCES)
+DIST_SOURCES = $(test_crypto_SOURCES) $(test_general_SOURCES) \
+	$(test_need_secrets_SOURCES) $(test_setting_8021x_SOURCES) \
+	$(test_settings_defaults_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+	html-recursive info-recursive install-data-recursive \
+	install-dvi-recursive install-exec-recursive \
+	install-html-recursive install-info-recursive \
+	install-pdf-recursive install-ps-recursive install-recursive \
+	installcheck-recursive installdirs-recursive pdf-recursive \
+	ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+	distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = certs
+INCLUDES = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util
+
+test_settings_defaults_SOURCES = \
+	test-settings-defaults.c
+
+test_settings_defaults_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_settings_defaults_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+test_crypto_SOURCES = \
+	test-crypto.c
+
+test_crypto_CPPFLAGS = \
+	$(GLIB_CFLAGS)
+
+test_crypto_LDADD = \
+	$(top_builddir)/libnm-util/libtest-crypto.la \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS)
+
+test_need_secrets_SOURCES = \
+	test-need-secrets.c
+
+test_need_secrets_CPPFLAGS = \
+	-DTEST_CERT_DIR=\"$(top_srcdir)/libnm-util/tests/certs/\" \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_need_secrets_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+test_general_SOURCES = \
+	test-general.c
+
+test_general_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_general_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+test_setting_8021x_SOURCES = \
+	test-setting-8021x.c
+
+test_setting_8021x_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_setting_8021x_LDADD = \
+	$(top_builddir)/libnm-util/libnm-util.la \
+	$(GLIB_LIBS) \
+	$(DBUS_LIBS)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libnm-util/tests/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu libnm-util/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+test-crypto$(EXEEXT): $(test_crypto_OBJECTS) $(test_crypto_DEPENDENCIES) 
+	@rm -f test-crypto$(EXEEXT)
+	$(LINK) $(test_crypto_OBJECTS) $(test_crypto_LDADD) $(LIBS)
+test-general$(EXEEXT): $(test_general_OBJECTS) $(test_general_DEPENDENCIES) 
+	@rm -f test-general$(EXEEXT)
+	$(LINK) $(test_general_OBJECTS) $(test_general_LDADD) $(LIBS)
+test-need-secrets$(EXEEXT): $(test_need_secrets_OBJECTS) $(test_need_secrets_DEPENDENCIES) 
+	@rm -f test-need-secrets$(EXEEXT)
+	$(LINK) $(test_need_secrets_OBJECTS) $(test_need_secrets_LDADD) $(LIBS)
+test-setting-8021x$(EXEEXT): $(test_setting_8021x_OBJECTS) $(test_setting_8021x_DEPENDENCIES) 
+	@rm -f test-setting-8021x$(EXEEXT)
+	$(LINK) $(test_setting_8021x_OBJECTS) $(test_setting_8021x_LDADD) $(LIBS)
+test-settings-defaults$(EXEEXT): $(test_settings_defaults_OBJECTS) $(test_settings_defaults_DEPENDENCIES) 
+	@rm -f test-settings-defaults$(EXEEXT)
+	$(LINK) $(test_settings_defaults_OBJECTS) $(test_settings_defaults_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-test-crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_general-test-general.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_need_secrets-test-need-secrets.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_setting_8021x-test-setting-8021x.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_settings_defaults-test-settings-defaults.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+test_crypto-test-crypto.o: test-crypto.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_crypto-test-crypto.o -MD -MP -MF $(DEPDIR)/test_crypto-test-crypto.Tpo -c -o test_crypto-test-crypto.o `test -f 'test-crypto.c' || echo '$(srcdir)/'`test-crypto.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_crypto-test-crypto.Tpo $(DEPDIR)/test_crypto-test-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-crypto.c' object='test_crypto-test-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_crypto-test-crypto.o `test -f 'test-crypto.c' || echo '$(srcdir)/'`test-crypto.c
+
+test_crypto-test-crypto.obj: test-crypto.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_crypto-test-crypto.obj -MD -MP -MF $(DEPDIR)/test_crypto-test-crypto.Tpo -c -o test_crypto-test-crypto.obj `if test -f 'test-crypto.c'; then $(CYGPATH_W) 'test-crypto.c'; else $(CYGPATH_W) '$(srcdir)/test-crypto.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_crypto-test-crypto.Tpo $(DEPDIR)/test_crypto-test-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-crypto.c' object='test_crypto-test-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_crypto_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_crypto-test-crypto.obj `if test -f 'test-crypto.c'; then $(CYGPATH_W) 'test-crypto.c'; else $(CYGPATH_W) '$(srcdir)/test-crypto.c'; fi`
+
+test_general-test-general.o: test-general.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_general_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_general-test-general.o -MD -MP -MF $(DEPDIR)/test_general-test-general.Tpo -c -o test_general-test-general.o `test -f 'test-general.c' || echo '$(srcdir)/'`test-general.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_general-test-general.Tpo $(DEPDIR)/test_general-test-general.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-general.c' object='test_general-test-general.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_general_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_general-test-general.o `test -f 'test-general.c' || echo '$(srcdir)/'`test-general.c
+
+test_general-test-general.obj: test-general.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_general_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_general-test-general.obj -MD -MP -MF $(DEPDIR)/test_general-test-general.Tpo -c -o test_general-test-general.obj `if test -f 'test-general.c'; then $(CYGPATH_W) 'test-general.c'; else $(CYGPATH_W) '$(srcdir)/test-general.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_general-test-general.Tpo $(DEPDIR)/test_general-test-general.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-general.c' object='test_general-test-general.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_general_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_general-test-general.obj `if test -f 'test-general.c'; then $(CYGPATH_W) 'test-general.c'; else $(CYGPATH_W) '$(srcdir)/test-general.c'; fi`
+
+test_need_secrets-test-need-secrets.o: test-need-secrets.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_need_secrets_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_need_secrets-test-need-secrets.o -MD -MP -MF $(DEPDIR)/test_need_secrets-test-need-secrets.Tpo -c -o test_need_secrets-test-need-secrets.o `test -f 'test-need-secrets.c' || echo '$(srcdir)/'`test-need-secrets.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_need_secrets-test-need-secrets.Tpo $(DEPDIR)/test_need_secrets-test-need-secrets.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-need-secrets.c' object='test_need_secrets-test-need-secrets.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_need_secrets_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_need_secrets-test-need-secrets.o `test -f 'test-need-secrets.c' || echo '$(srcdir)/'`test-need-secrets.c
+
+test_need_secrets-test-need-secrets.obj: test-need-secrets.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_need_secrets_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_need_secrets-test-need-secrets.obj -MD -MP -MF $(DEPDIR)/test_need_secrets-test-need-secrets.Tpo -c -o test_need_secrets-test-need-secrets.obj `if test -f 'test-need-secrets.c'; then $(CYGPATH_W) 'test-need-secrets.c'; else $(CYGPATH_W) '$(srcdir)/test-need-secrets.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_need_secrets-test-need-secrets.Tpo $(DEPDIR)/test_need_secrets-test-need-secrets.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-need-secrets.c' object='test_need_secrets-test-need-secrets.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_need_secrets_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_need_secrets-test-need-secrets.obj `if test -f 'test-need-secrets.c'; then $(CYGPATH_W) 'test-need-secrets.c'; else $(CYGPATH_W) '$(srcdir)/test-need-secrets.c'; fi`
+
+test_setting_8021x-test-setting-8021x.o: test-setting-8021x.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_setting_8021x_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_setting_8021x-test-setting-8021x.o -MD -MP -MF $(DEPDIR)/test_setting_8021x-test-setting-8021x.Tpo -c -o test_setting_8021x-test-setting-8021x.o `test -f 'test-setting-8021x.c' || echo '$(srcdir)/'`test-setting-8021x.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_setting_8021x-test-setting-8021x.Tpo $(DEPDIR)/test_setting_8021x-test-setting-8021x.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-setting-8021x.c' object='test_setting_8021x-test-setting-8021x.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_setting_8021x_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_setting_8021x-test-setting-8021x.o `test -f 'test-setting-8021x.c' || echo '$(srcdir)/'`test-setting-8021x.c
+
+test_setting_8021x-test-setting-8021x.obj: test-setting-8021x.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_setting_8021x_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_setting_8021x-test-setting-8021x.obj -MD -MP -MF $(DEPDIR)/test_setting_8021x-test-setting-8021x.Tpo -c -o test_setting_8021x-test-setting-8021x.obj `if test -f 'test-setting-8021x.c'; then $(CYGPATH_W) 'test-setting-8021x.c'; else $(CYGPATH_W) '$(srcdir)/test-setting-8021x.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_setting_8021x-test-setting-8021x.Tpo $(DEPDIR)/test_setting_8021x-test-setting-8021x.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-setting-8021x.c' object='test_setting_8021x-test-setting-8021x.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_setting_8021x_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_setting_8021x-test-setting-8021x.obj `if test -f 'test-setting-8021x.c'; then $(CYGPATH_W) 'test-setting-8021x.c'; else $(CYGPATH_W) '$(srcdir)/test-setting-8021x.c'; fi`
+
+test_settings_defaults-test-settings-defaults.o: test-settings-defaults.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_settings_defaults_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_settings_defaults-test-settings-defaults.o -MD -MP -MF $(DEPDIR)/test_settings_defaults-test-settings-defaults.Tpo -c -o test_settings_defaults-test-settings-defaults.o `test -f 'test-settings-defaults.c' || echo '$(srcdir)/'`test-settings-defaults.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_settings_defaults-test-settings-defaults.Tpo $(DEPDIR)/test_settings_defaults-test-settings-defaults.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-settings-defaults.c' object='test_settings_defaults-test-settings-defaults.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_settings_defaults_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_settings_defaults-test-settings-defaults.o `test -f 'test-settings-defaults.c' || echo '$(srcdir)/'`test-settings-defaults.c
+
+test_settings_defaults-test-settings-defaults.obj: test-settings-defaults.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_settings_defaults_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_settings_defaults-test-settings-defaults.obj -MD -MP -MF $(DEPDIR)/test_settings_defaults-test-settings-defaults.Tpo -c -o test_settings_defaults-test-settings-defaults.obj `if test -f 'test-settings-defaults.c'; then $(CYGPATH_W) 'test-settings-defaults.c'; else $(CYGPATH_W) '$(srcdir)/test-settings-defaults.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/test_settings_defaults-test-settings-defaults.Tpo $(DEPDIR)/test_settings_defaults-test-settings-defaults.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-settings-defaults.c' object='test_settings_defaults-test-settings-defaults.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_settings_defaults_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_settings_defaults-test-settings-defaults.obj `if test -f 'test-settings-defaults.c'; then $(CYGPATH_W) 'test-settings-defaults.c'; else $(CYGPATH_W) '$(srcdir)/test-settings-defaults.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+	@fail= failcom='exit 1'; \
+	for f in x $$MAKEFLAGS; do \
+	  case $$f in \
+	    *=* | --[!k]*);; \
+	    *k*) failcom='fail=yes';; \
+	  esac; \
+	done; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(MKDIR_P) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+@WITH_TESTS_FALSE@check-local:
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-local
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) check-am \
+	ctags-recursive install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+	all all-am check check-am check-local clean clean-generic \
+	clean-libtool clean-noinstPROGRAMS ctags ctags-recursive \
+	distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-recursive uninstall uninstall-am
+
+
+@WITH_TESTS_TRUE@check-local: test-settings-defaults test-crypto test-need-secrets
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-settings-defaults
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-need-secrets
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-general
+
+# Private key and CA certificate in the same file (PEM)
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-setting-8021x \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test_key_and_cert.pem \
+@WITH_TESTS_TRUE@		"test" \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test-key-only-decrypted.der
+
+# Private key by itself (PEM)
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-setting-8021x \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test-key-only.pem \
+@WITH_TESTS_TRUE@		"test" \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test-key-only-decrypted.der
+
+# Private key and CA certificate in the same file (pkcs12)
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-setting-8021x \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test-cert.p12 \
+@WITH_TESTS_TRUE@		"test"
+
+# Normal CA certificate
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --cert \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test_ca_cert.pem
+
+# Another CA certificate
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --cert \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test2_ca_cert.pem
+
+# CA certificate without an ending newline
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --cert \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/ca-no-ending-newline.pem
+
+# Combined user cert and private key
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --cert \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test_key_and_cert.pem
+
+# Another combined user cert and private key
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --cert \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test2_key_and_cert.pem
+
+# Private key with 8 bytes of tail padding
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --key \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test_key_and_cert.pem \
+@WITH_TESTS_TRUE@		"test"
+
+# Private key with 6 bytes of tail padding
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --key \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test2_key_and_cert.pem \
+@WITH_TESTS_TRUE@		"12345testing"
+
+# PKCS#12 file
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --p12 \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test-cert.p12 \
+@WITH_TESTS_TRUE@		"test"
+
+# Another PKCS#12 file
+@WITH_TESTS_TRUE@	$(abs_builddir)/test-crypto --p12 \
+@WITH_TESTS_TRUE@		$(top_srcdir)/libnm-util/tests/certs/test2-cert.p12 \
+@WITH_TESTS_TRUE@		"12345testing"
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libnm-util/tests/certs/Makefile.am b/libnm-util/tests/certs/Makefile.am
new file mode 100644
index 00000000..4cd8a24b
--- /dev/null
+++ b/libnm-util/tests/certs/Makefile.am
@@ -0,0 +1,20 @@
+# test-cert.p12 created with:
+#
+# openssl pkcs12 -export \
+#	-in test_key_and_cert.pem \
+#	-inkey test_key_and_cert.pem \
+#	-certfile test_ca_cert.pem \
+#	-name "test-pkcs12" \
+#	-out test-cert.p12
+
+EXTRA_DIST = \
+	test_ca_cert.pem \
+	test_key_and_cert.pem \
+	test-cert.p12 \
+	test2_ca_cert.pem \
+	test2_key_and_cert.pem \
+	test2-cert.p12 \
+	ca-no-ending-newline.pem \
+	test-key-only.pem \
+	test-key-only-decrypted.der
+
diff --git a/libnm-util/tests/certs/Makefile.in b/libnm-util/tests/certs/Makefile.in
new file mode 100644
index 00000000..18e4af87
--- /dev/null
+++ b/libnm-util/tests/certs/Makefile.in
@@ -0,0 +1,437 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# test-cert.p12 created with:
+#
+# openssl pkcs12 -export \
+#	-in test_key_and_cert.pem \
+#	-inkey test_key_and_cert.pem \
+#	-certfile test_ca_cert.pem \
+#	-name "test-pkcs12" \
+#	-out test-cert.p12
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libnm-util/tests/certs
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMODULE_CFLAGS = @GMODULE_CFLAGS@
+GMODULE_LIBS = @GMODULE_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+IPTABLES_PATH = @IPTABLES_PATH@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBM = @LIBM@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+RANLIB = @RANLIB@
+RESOLVCONF_PATH = @RESOLVCONF_PATH@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = \
+	test_ca_cert.pem \
+	test_key_and_cert.pem \
+	test-cert.p12 \
+	test2_ca_cert.pem \
+	test2_key_and_cert.pem \
+	test2-cert.p12 \
+	ca-no-ending-newline.pem \
+	test-key-only.pem \
+	test-key-only-decrypted.der
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libnm-util/tests/certs/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu libnm-util/tests/certs/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+	distclean distclean-generic distclean-libtool distdir dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libnm-util/tests/certs/ca-no-ending-newline.pem b/libnm-util/tests/certs/ca-no-ending-newline.pem
new file mode 100644
index 00000000..664e299a
--- /dev/null
+++ b/libnm-util/tests/certs/ca-no-ending-newline.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----

+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD

+VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv

+bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv

+b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV

+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU

+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds

+b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH

+iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS

+r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4

+04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r

+GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9

+3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P

+lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/

+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/libnm-util/tests/certs/test-cert.p12 b/libnm-util/tests/certs/test-cert.p12
new file mode 100644
index 00000000..ae4a6830
--- /dev/null
+++ b/libnm-util/tests/certs/test-cert.p12
Binary files differdiff --git a/libnm-util/tests/certs/test-key-only-decrypted.der b/libnm-util/tests/certs/test-key-only-decrypted.der
new file mode 100644
index 00000000..c8652537
--- /dev/null
+++ b/libnm-util/tests/certs/test-key-only-decrypted.der
Binary files differdiff --git a/libnm-util/tests/certs/test-key-only.pem b/libnm-util/tests/certs/test-key-only.pem
new file mode 100644
index 00000000..5ef530e9
--- /dev/null
+++ b/libnm-util/tests/certs/test-key-only.pem
@@ -0,0 +1,92 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,B29FCA6ECB7C0C48
+
+KuRV4sEVQkY0r0E+E81Se2qpm54HeboUMIJcMVqAt/HIeYwpjEzmXHUywXgayA5k
+r5Uvru95vymFz80q/rl6i4Mt3ckEQ3/VHmBI4pPj/JeRbiDhqTyLqpB5zMgbuti+
+PsEemlbiaCMZFdJk8rJBWADTkirGbXs5ciLwTJyrJXhkBVTqoHtphSWMELxcrnQj
+5bAXZ+rnApWA7LR3yCY1UE0Nj39Yu8lkzDJtbsSXPJVO//OQiGuQRcY6roAUzeS/
+amLdIYqoZm3OF83Ro88XZCIDCG6XjgKoqojtekClVIytB6mHWk+IHhI7DgU/pDBj
+yOskzVRMja9ECnqaVX+bmlmJGZBgcd7+XiebCXNY9IAx/ZQDMbDJDoJusvH6npJR
+PBgag8QBSzwpFyEP/1LrlyVyggZcGuXDWfUiM2rt2UvLm8QYeT5WGfUI+QwW6JRV
+xEz2QwRiPsmj0uK2fXLKHbY0Pv79IMLqgesCAUbndWsi94kjAhpJGa/WfKVb8Dpg
+T+mVa2OT0pgewPWJ3h5f47ag27WDJthsIUHAvNZ03uM23DIqDh6o03/B3/4r1Uab
+HzsLWhUOzbLihO08qcPk/JqtDVCv579mpgvy7bL2ZsZUzefnmg+ah+1bhLguryZu
+Zrt1hTu5WTz27UbYwWNtkxWnWcM9sSF9y+KVwumYhIUGTvg3Jds/jmp/Z5ad8tev
+0RQuWmqnXVD51Y92tOXobJH7JwGbQ4GeRNG/UX7DS9Lvb6rPgmmxvGbXMcjEMapB
+m5DLjCD7Lz4++sfzhCic3/nL8e8fjoYZmCbL/SpYjGjTrfoNaGYaT0De8MxCSjPf
+rNT+ldWFD0oAOTmT0uqOFkeIx3vxczSQUyRfzGOLqtb9mAXk8lvdb8Myb5atfe8f
+FNBL5ocz6Szv2AmKmjZE5P6NKSllyryCYl+H2QU0e3OyY8S9aG2aG8fqeVKaA01S
+uOOjsK6IKMpkit+VFDP7RWvS8p1FTwTVRdbMZzASW8+D98OHsTlOENtQuuFyvNaN
+vPHrbHltTkwDlYVab9UpFr/ZRW3cNJrmcl0hyeCJxk5u8B9UwW6yZmd24hxda2xt
+1QjnxfWlX0i5KTmfWFyPmRYt1WH9rdVaH6wqRbGNbMRT7Lez0E/2+LXjFiiEe0Vh
+ZuryhyhUXpHY8yyHxFY92XT504Z3wPUaJ1tQxGhdziZ7HcqFT8f4oTNJdBNtzqNw
+l63gSTaKUkSB14CBOyR84cqpgSMwZvWZb3RQFYY4GSALWwhnn6PWeRhjWAXPms2v
+tRmfEyDUCyPxDarezvS9kUir3XbvnCkoJOrK/lw4a8ygGBCBqRKOdI0SRUf1Nnjm
+aZBrKrXHv5vP66fyC7cGMTq35+iiHbE//rRDGPrf9vpmw3ki3FlDMyAXOC5yWDco
+0n+cNDyHJDdIW3+3nfZvSgPPMwmTdfNO90X3YczSmMTGjnIuhQMf7FMFmMZOSrqD
+u234vYCjIIlph3aVpbzjOqKg3d4nuP9oouyAgRAr+vShgHRmq0jvsAiGH3Q6RqEj
+DvkM1NgoloMy5RhKF4hIeaQM/UlgoXrcEx0RGCJHykQcQmVa5cf5EwjrO7b+w2Lg
+-----END RSA PRIVATE KEY-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+        Signature Algorithm: md5WithRSAEncryption
+        Issuer: C=US, ST=Berkshire, L=Newbury, O=My Company Ltd, OU=test, CN=test/emailAddress=test@test.com
+        Validity
+            Not Before: Feb  2 02:22:25 2009 GMT
+            Not After : Jan 31 02:22:25 2019 GMT
+        Subject: C=US, ST=Berkshire, O=My Company Ltd, OU=test-client, CN=test-client/emailAddress=test-client@test.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:ce:d2:e9:fb:f4:a4:ce:66:87:cc:8b:2d:d2:c3:
+                    b8:fa:be:ea:53:08:95:9e:27:4a:04:78:31:a1:36:
+                    d2:e5:42:f5:76:f7:24:71:b8:d3:10:fd:b7:57:86:
+                    68:29:02:1b:e9:93:2b:af:c0:7e:3d:58:16:34:10:
+                    25:85:7e:28:2d:42:fc:34:7f:d6:4f:0a:e7:b6:b9:
+                    40:51:c8:e5:89:60:cd:73:51:63:67:e1:59:36:31:
+                    5e:d0:87:4b:5d:97:8a:42:22:d3:42:f2:83:62:79:
+                    84:45:5b:d8:20:97:e2:d1:34:23:5e:ec:bf:8a:41:
+                    7c:ad:80:50:f3:9b:1c:51:5d:e4:29:1c:c1:c6:7c:
+                    a1:00:70:0f:f6:79:77:03:22:f5:4b:ab:76:ca:20:
+                    ab:e1:4e:53:6c:86:68:e9:d6:0f:6e:95:4b:b5:b5:
+                    cf:d3:6c:d4:73:db:c0:3d:1d:20:20:72:ab:3e:25:
+                    bb:3f:cd:5f:2d:77:fd:d7:0b:a8:eb:0c:73:c0:3f:
+                    74:27:17:56:cf:31:9a:a9:ed:c1:39:27:c7:aa:e9:
+                    77:48:f4:6d:56:89:35:5c:79:5b:f6:2f:8c:9f:93:
+                    bc:13:1c:d0:74:5f:30:1b:15:43:71:8e:ff:1a:df:
+                    65:65:69:c2:8a:5a:ab:04:4a:80:de:20:29:8d:48:
+                    1c:83
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                34:E9:5C:D8:FC:C7:AF:F7:5D:F4:13:D3:82:1A:CD:52:0F:4A:BB:0B
+            X509v3 Authority Key Identifier: 
+                keyid:52:33:2E:22:66:79:82:E5:41:2E:8D:3B:B2:E5:CC:FA:BE:99:33:8E
+                DirName:/C=US/ST=Berkshire/L=Newbury/O=My Company Ltd/OU=test/CN=test/emailAddress=test@test.com
+                serial:E6:4C:5C:96:26:5E:D6:D0
+
+    Signature Algorithm: md5WithRSAEncryption
+        88:c4:dd:c5:4a:a6:8e:1f:36:62:80:31:6b:2b:85:34:0d:6c:
+        a7:dc:e1:5a:3c:dd:74:57:db:59:be:8b:f2:79:40:2d:bd:9b:
+        e1:ab:0e:03:75:20:75:2f:ea:97:f9:d4:7e:15:75:c2:82:f5:
+        b1:37:e4:8b:24:d4:ef:02:3b:19:6a:56:bb:9f:c3:a5:b4:c4:
+        39:a0:64:96:5b:c3:a8:19:74:ec:0b:cf:33:df:f3:12:f3:e2:
+        ab:1d:7d:de:21:64:cd:b1:a0:6f:7a:77:84:d4:62:8a:50:e6:
+        76:5d:af:3a:ef:1a:87:0e:1a:38:c4:f9:0d:2f:14:e0:20:32:
+        4c:2e:70:d2:71:82:d5:e5:c9:13:2c:4e:b4:c8:63:65:1d:48:
+        e8:c3:56:6d:88:84:57:65:13:1e:42:48:b4:03:25:71:4e:12:
+        9d:1a:88:65:d5:71:21:3b:0e:be:37:1c:f0:88:6b:45:0c:0e:
+        6c:e5:60:cb:86:bf:bf:40:30:c4:cf:94:f8:1c:60:d4:1b:df:
+        88:02:7a:45:7e:d1:c6:f3:07:b9:b1:8f:2a:55:2f:be:77:9f:
+        d1:46:64:05:b7:67:c5:b2:ac:8c:42:8f:6a:51:a4:0d:2b:16:
+        a4:d6:39:a5:f8:25:ed:68:25:13:b5:19:ac:6c:e9:ec:ee:03:
+        6b:80:38:6e
+
diff --git a/libnm-util/tests/certs/test2-cert.p12 b/libnm-util/tests/certs/test2-cert.p12
new file mode 100644
index 00000000..9d5732b0
--- /dev/null
+++ b/libnm-util/tests/certs/test2-cert.p12
Binary files differdiff --git a/libnm-util/tests/certs/test2_ca_cert.pem b/libnm-util/tests/certs/test2_ca_cert.pem
new file mode 100644
index 00000000..9a487ca4
--- /dev/null
+++ b/libnm-util/tests/certs/test2_ca_cert.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEpDCCA4ygAwIBAgIJANDnVhixAO1GMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD
+VQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czERMA8GA1UEBxMIV2VzdGZv
+cmQxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5n
+MRAwDgYDVQQDEwdlYXB0ZXN0MRgwFgYJKoZIhvcNAQkBFglpdEBpdC5jb20wHhcN
+MDcxMTA5MTU0ODI1WhcNMTcxMTA2MTU0ODI1WjCBkjELMAkGA1UEBhMCVVMxFjAU
+BgNVBAgTDU1hc3NhY2h1c2V0dHMxETAPBgNVBAcTCFdlc3Rmb3JkMRYwFAYDVQQK
+Ew1SZWQgSGF0LCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEQMA4GA1UEAxMH
+ZWFwdGVzdDEYMBYGCSqGSIb3DQEJARYJaXRAaXQuY29tMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAz9zRLSiQyQangDgEliEP8xSpnPJS7GjXzrkZS3sk
+gZLuVuwoFeZRq3Hsrq/wGd/vM0KUFNmEaMc+47jnuv0UHQcQ45ZACO7s4/Aflhzj
+lkmud/z06hVknIzjXmvS6q2ttCviHsXnfokl+wAxuUhsd+le0xjP9H1jXny4YBuS
+jP+yGUz7PL4w1sFFghKIPrlB7m4GkFbQRqvH7FSJg86GWopPwJvNvIzhOZiO1a1D
+CAAL4Ru3jxtNFxqWT87C/qUEe/2Qb7jtNyqFcKfwZyZh4u1bo0c8bjErlUZERbWz
+zM3hTFypuw+i2v+0h3A8/Xb0hTjcHkUoJgfSdbsOLC5TOwIDAQABo4H6MIH3MB0G
+A1UdDgQWBBR+UOaH4e8nrEuMcEXJl7UN5r/wDTCBxwYDVR0jBIG/MIG8gBR+UOaH
+4e8nrEuMcEXJl7UN5r/wDaGBmKSBlTCBkjELMAkGA1UEBhMCVVMxFjAUBgNVBAgT
+DU1hc3NhY2h1c2V0dHMxETAPBgNVBAcTCFdlc3Rmb3JkMRYwFAYDVQQKEw1SZWQg
+SGF0LCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEQMA4GA1UEAxMHZWFwdGVz
+dDEYMBYGCSqGSIb3DQEJARYJaXRAaXQuY29tggkA0OdWGLEA7UYwDAYDVR0TBAUw
+AwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAmE2jqUymfxN2Vv7bPafoK/EpZwGPxu+z
+phRFsgUgWVzidc/GtOxN81LduJ+ow8MEbQIabo4JV/MdKzuPuhAHToAQdeb0LIWa
+p59vTIZiVhUt0cMAbQwKcTnfmDnXw9wytvtKgeAXJq0Jd6F+uNXTiR1btlYLZqmF
+oSu54cHQlXpUT9z0BnQ8eXd7m0TwfzGQkTHQI7xBa87lZDAkJaTlhv7fR5vPmJYY
+0LiXii71ce+4hxdlp7hQfwQ2sb8FPY3RlVboTRD0CvGaWypWhdSZnS790dBXgZOs
+NCge6NGuHzW5LtiZE9ppuv8qJysVcIFdAqt8dkx58ksOqFcARCerXw==
+-----END CERTIFICATE-----
diff --git a/libnm-util/tests/certs/test2_key_and_cert.pem b/libnm-util/tests/certs/test2_key_and_cert.pem
new file mode 100644
index 00000000..a668596e
--- /dev/null
+++ b/libnm-util/tests/certs/test2_key_and_cert.pem
@@ -0,0 +1,119 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,5FA2D6D6242C26D0
+
+dyNdbh115sczbUEhiaGYJ6fazyvJss5thPFEmkP6aftYlvXY6vPtc++xFCCktiSd
+qFVEyi6oDyV4iGPmX7pCJ0e+pSI6uFNXKFtxh5/+/wXZcOEMCvfu7w2IrvYF2LHY
+qJDljcISSRxeINuYO7TETD5fLLRKj2X9vwwkwVN02b2N5jsrm6Bt//WbatqSB3ln
+FHyQhVKkvdl9Hr1XNmEfgGfZSxxDoPu1DjhtZ5ja2LZj64C2CXdI0oq2wcAVvQNn
+rZeeg9sinQJkz9rwsNaWqlYw4X+YD2JRSwZuvwkWRydYMwgb1XS/jCxtuFF8NXWP
+RBAOAZZUy7onzohsJHVVa05wCKQ4klo+PEfI3vn7BeuHyciCc0eFqGRvz8eFDybH
+ZdPbU/3vGp+mOB7gd27TptttTCQQy9uM5CIyovNSYsAIw1Z583Ea4q8eXgzkgD6D
+isCqkGXMfPbNXU3myQGDnQwWRi2CqX+rXM8PJUhdewLAlmHRz/aYSuql2BRixJKx
+eASzmFBYdAjrvafda5D+xTyJwXEwdq/HlqMK9cY28ZbNrzA2Kor2X23EKC1+VG8k
+B67OsfUhW27j4u6aV5JdLf87OtF3mHFRR+Lzs7i7LYvJ8ACE+jiIi7PboZjK5Oiv
+JqTK0BwDaeNYjkd6jiJh8It/ReMbLk65J3eldOklN0VMPYiqcQnHvSPC2DD1YAy+
+Rv/JVj6TvzvgEAj+hgH6MAAF6u3ARj6+10DlvhUubkOC5RztLKReu8B+427TuuDb
+T03gFpHD6X9IqSiq/QfYFyHFojCVSrv6wDZOcHc1s71kpJ8R14YIVe+DrrZN/0D4
+M631jdNg3JARMZXcXTHrghGIdPmOtrsRyTTRZuGoVup/DW9MRzOzCTMSNCX8T+eq
+13HMSNQEO9lMwy0sYeO5c7sjHY4K1ubZuVE1mvXq4JLz3YxXJIvgp8TUvqDnAsK+
+Fv63bDoTg5Tq63XvnaKc7Lawneyg5ZAMzPN3nM0/1EZcn/2ICI5c4Yepc5t63EI5
+KytuXx86Mcx234enj3uMeuM22POQ1SnKOef6dFzK/CE8J8eUEY/aDhX4eBl/s3nd
+U4+aaFKYz3HTazePayt2SC6rP/KKMmS14q59bOQA1DiWxCvmA2ypRyP87fV5DstH
+I53RD5xp1P38iaO8U/divD0W2dkv748s9DQqYrHPtWALT9esxNU07CgB8Zt070si
+7pzjQ8FDCZ8ygDmwWGNSBz1nA90Cpd6gAFDrep7HAtDE4qgNfokycpaJZkXBei/U
+tC4tWYRbqDEsEbeBHvQRJzzqWzk9e/P4fQoelM3aryKzKLG5z7KvywVifKMLECQ+
+tIpzoRp06nuTA/O+iLFdkCy3JEWszfvvOwTwtIIV6+3s8TU0k9MmzEe3rGL+QqT/
+Tf+9/dN5LK+LWyc99BfmCOrBuFtQmHyEXkfe6EuFYEwj0B2ZfnLCon6cdRujjK7H
+IJslC1B/cBVqG3KCrbBzjeygKfJ5Ijo72oXZJOCFTLeJefZKGGWJCp9nG9h9Wrcf
+fEN/mj3wBvTa90/PYFj9NuaBtrvMF8Rn9XDeYPq2JGL8YkNdPuO8A+2Yko8wcvST
+-----END RSA PRIVATE KEY-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+        Signature Algorithm: md5WithRSAEncryption
+        Issuer: C=US, ST=Massachusetts, L=Westford, O=Red Hat, Inc., OU=Engineering, CN=eaptest/emailAddress=it@it.com
+        Validity
+            Not Before: Nov  9 15:50:14 2007 GMT
+            Not After : Nov  6 15:50:14 2017 GMT
+        Subject: C=US, ST=Massachusetts, O=Red Hat, Inc., OU=Engineering, CN=client/emailAddress=it@it.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:b0:8f:4f:1c:93:d4:43:e7:87:b7:22:33:55:a8:
+                    35:a1:c4:01:b0:f1:ed:26:23:96:ab:65:c2:c2:54:
+                    db:79:22:03:ad:3f:6f:22:e3:63:3f:f4:21:6d:fa:
+                    88:c8:8f:1a:ce:55:49:7c:98:33:6a:67:8a:8d:d9:
+                    34:b0:c3:42:f4:72:a4:45:43:05:72:5d:0c:d3:42:
+                    f8:9c:66:3b:b8:f8:77:ea:f6:b6:94:d7:cc:5d:62:
+                    34:2a:14:48:0a:bc:65:94:f5:7a:63:98:6c:88:4c:
+                    25:d8:95:f1:40:3d:00:d2:fb:43:28:fa:02:fb:2c:
+                    80:b3:e1:33:e7:8c:ce:8a:a0:1b:3d:04:4d:bc:a1:
+                    b6:a2:42:8b:8e:f3:5b:4a:72:34:7d:8d:ba:d8:46:
+                    22:35:da:5c:f8:dd:fc:6d:9e:59:22:b7:6b:e7:78:
+                    56:54:9f:4c:d1:e2:4a:23:a3:bc:04:ea:46:6b:70:
+                    8a:fb:fe:8a:73:ca:36:d5:f3:e9:17:e3:22:d5:b3:
+                    70:05:e7:f7:37:b7:21:b5:90:53:27:27:ea:36:9b:
+                    00:ff:35:b0:66:3d:dc:a9:2f:95:d2:21:18:98:4f:
+                    28:07:09:70:20:a8:b1:82:aa:a5:df:ae:0f:e3:36:
+                    be:68:8c:9e:80:d3:33:d0:f5:84:17:d9:0f:eb:9d:
+                    af:0b
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                71:AB:BB:91:B7:04:DE:43:35:36:07:8A:35:CA:BE:5C:3E:EB:B1:09
+            X509v3 Authority Key Identifier: 
+                keyid:7E:50:E6:87:E1:EF:27:AC:4B:8C:70:45:C9:97:B5:0D:E6:BF:F0:0D
+                DirName:/C=US/ST=Massachusetts/L=Westford/O=Red Hat, Inc./OU=Engineering/CN=eaptest/emailAddress=it@it.com
+                serial:D0:E7:56:18:B1:00:ED:46
+
+    Signature Algorithm: md5WithRSAEncryption
+        ce:43:6d:f7:f8:4a:66:fd:8a:2c:41:a6:e0:03:0e:60:30:d4:
+        41:01:ba:46:ba:81:97:64:68:83:25:9c:e1:2c:03:8b:2d:ca:
+        85:cf:bc:fa:ca:22:c4:59:28:23:8f:ff:50:94:60:1c:90:dd:
+        75:f4:d4:ea:8c:fa:61:61:08:35:4a:8f:aa:a7:e9:3d:76:e9:
+        08:28:55:01:c4:03:42:c7:ad:58:bb:ee:94:f7:09:b3:9a:9b:
+        8b:d0:25:95:18:a6:22:d5:2c:fc:b7:bb:91:0c:7c:03:7f:9b:
+        85:de:b0:e4:95:a8:73:94:27:0a:11:4e:e3:67:ae:2b:cc:e7:
+        51:29:10:23:57:5c:3e:e7:ea:47:e0:f0:8f:5b:a2:9f:26:cf:
+        7f:b5:7c:44:b1:7b:83:67:3c:41:ae:c6:66:64:e0:d2:ef:57:
+        a4:5c:1b:94:11:ce:28:e5:91:51:ef:e1:98:b7:3b:9a:cc:f7:
+        b9:85:76:eb:a8:2b:15:4a:cc:1a:a3:42:fa:be:1c:ce:b8:eb:
+        ee:12:d7:2f:e4:a8:cf:eb:2a:8f:78:e8:91:88:fa:c2:98:75:
+        6a:4c:92:3f:2e:0d:e1:20:39:36:c6:2c:be:67:30:c3:f3:c3:
+        65:81:ac:e3:3c:19:6a:21:ee:ea:f5:22:66:74:b2:07:53:7c:
+        9a:0c:24:a6
+-----BEGIN CERTIFICATE-----
+MIIEtDCCA5ygAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBkjELMAkGA1UEBhMCVVMx
+FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxETAPBgNVBAcTCFdlc3Rmb3JkMRYwFAYD
+VQQKEw1SZWQgSGF0LCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEQMA4GA1UE
+AxMHZWFwdGVzdDEYMBYGCSqGSIb3DQEJARYJaXRAaXQuY29tMB4XDTA3MTEwOTE1
+NTAxNFoXDTE3MTEwNjE1NTAxNFowfjELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1h
+c3NhY2h1c2V0dHMxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xFDASBgNVBAsTC0Vu
+Z2luZWVyaW5nMQ8wDQYDVQQDEwZjbGllbnQxGDAWBgkqhkiG9w0BCQEWCWl0QGl0
+LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALCPTxyT1EPnh7ci
+M1WoNaHEAbDx7SYjlqtlwsJU23kiA60/byLjYz/0IW36iMiPGs5VSXyYM2pnio3Z
+NLDDQvRypEVDBXJdDNNC+JxmO7j4d+r2tpTXzF1iNCoUSAq8ZZT1emOYbIhMJdiV
+8UA9ANL7Qyj6AvssgLPhM+eMzoqgGz0ETbyhtqJCi47zW0pyNH2NuthGIjXaXPjd
+/G2eWSK3a+d4VlSfTNHiSiOjvATqRmtwivv+inPKNtXz6RfjItWzcAXn9ze3IbWQ
+Uycn6jabAP81sGY93KkvldIhGJhPKAcJcCCosYKqpd+uD+M2vmiMnoDTM9D1hBfZ
+D+udrwsCAwEAAaOCASYwggEiMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9w
+ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRxq7uRtwTeQzU2
+B4o1yr5cPuuxCTCBxwYDVR0jBIG/MIG8gBR+UOaH4e8nrEuMcEXJl7UN5r/wDaGB
+mKSBlTCBkjELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxETAP
+BgNVBAcTCFdlc3Rmb3JkMRYwFAYDVQQKEw1SZWQgSGF0LCBJbmMuMRQwEgYDVQQL
+EwtFbmdpbmVlcmluZzEQMA4GA1UEAxMHZWFwdGVzdDEYMBYGCSqGSIb3DQEJARYJ
+aXRAaXQuY29tggkA0OdWGLEA7UYwDQYJKoZIhvcNAQEEBQADggEBAM5Dbff4Smb9
+iixBpuADDmAw1EEBuka6gZdkaIMlnOEsA4styoXPvPrKIsRZKCOP/1CUYByQ3XX0
+1OqM+mFhCDVKj6qn6T126QgoVQHEA0LHrVi77pT3CbOam4vQJZUYpiLVLPy3u5EM
+fAN/m4XesOSVqHOUJwoRTuNnrivM51EpECNXXD7n6kfg8I9bop8mz3+1fESxe4Nn
+PEGuxmZk4NLvV6RcG5QRzijlkVHv4Zi3O5rM97mFduuoKxVKzBqjQvq+HM646+4S
+1y/kqM/rKo946JGI+sKYdWpMkj8uDeEgOTbGLL5nMMPzw2WBrOM8GWoh7ur1ImZ0
+sgdTfJoMJKY=
+-----END CERTIFICATE-----
diff --git a/libnm-util/tests/certs/test_ca_cert.pem b/libnm-util/tests/certs/test_ca_cert.pem
new file mode 100644
index 00000000..faa3fc29
--- /dev/null
+++ b/libnm-util/tests/certs/test_ca_cert.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEhjCCA26gAwIBAgIJAOZMXJYmXtbQMA0GCSqGSIb3DQEBBQUAMIGIMQswCQYD
+VQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcw
+FQYDVQQKEw5NeSBDb21wYW55IEx0ZDENMAsGA1UECxMEdGVzdDENMAsGA1UEAxME
+dGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0wOTAyMDIwMjIx
+NTJaFw0xOTAxMzEwMjIxNTJaMIGIMQswCQYDVQQGEwJVUzESMBAGA1UECBMJQmVy
+a3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5NeSBDb21wYW55IEx0
+ZDENMAsGA1UECxMEdGVzdDENMAsGA1UEAxMEdGVzdDEcMBoGCSqGSIb3DQEJARYN
+dGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANZz
+iHfFR6Gqdz5Gq1elSaf7J2tznfialPyMkRoqngSItrM3JWlkfBu3ZIpGSYG8+Z8H
+JjaKh/zkCCH73HcjADCocm/XtgxlOlccDtBvfDqifb2dBikcmcfh0tX0o4JU9L+z
+WP7sVcUkJG7C5811lA5pAsLVooJxrZnrta6XstGvFgTSxI2Y4Jowf0syM5b/AHIQ
++htsVhSyj1DWM9day3QsEMZl55SCxCLTB6ZIJLF6NmTYwdnzWWTQf4k7kcptZJU8
+Fqv60b/ATvvUf7S+ZMGLopFIIlmc1Kkn7VTseIKy4ycYuDpyimQy3Yw4flC00vhl
+hTijH+5dlVOUFrRh0bsCAwEAAaOB8DCB7TAdBgNVHQ4EFgQUUjMuImZ5guVBLo07
+suXM+r6ZM44wgb0GA1UdIwSBtTCBsoAUUjMuImZ5guVBLo07suXM+r6ZM46hgY6k
+gYswgYgxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAOBgNVBAcT
+B05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMQ0wCwYDVQQLEwR0ZXN0
+MQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3QuY29tggkA
+5kxcliZe1tAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAZsWzGp9D
+10NWf8ts3A9mhcEb8oDgKNpCOZF9gqr2usZygJjY9MX2n0n81T8XaWDk1wZLlcDW
+hzIalCY8rpE57Dj3MEmdCmYr3BrAy6GVlc/MO7ffTHxogl9ouScSYo8ETTtNMKCg
+zwkDnXjGzmb1e59DIXMVHBBbTmJXJbM8XP9A2azJkVUyE2fSdWksXMP0XklGbW2p
+DksJ+8G1IWkyRhnINV5GZLLNAT5gBA6sUv/iqQKwk8r2dsweMPtLoLWZPAaZErYh
+sthGy00Q0GJkmkJpQl8QIlPq5vylNcMUhvc8tmzwk+rDVUj3jTJWqFwMtnHAaNyC
+FXl9ynWpe5J28w==
+-----END CERTIFICATE-----
diff --git a/libnm-util/tests/certs/test_key_and_cert.pem b/libnm-util/tests/certs/test_key_and_cert.pem
new file mode 100644
index 00000000..c00495d0
--- /dev/null
+++ b/libnm-util/tests/certs/test_key_and_cert.pem
@@ -0,0 +1,119 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,B29FCA6ECB7C0C48
+
+KuRV4sEVQkY0r0E+E81Se2qpm54HeboUMIJcMVqAt/HIeYwpjEzmXHUywXgayA5k
+r5Uvru95vymFz80q/rl6i4Mt3ckEQ3/VHmBI4pPj/JeRbiDhqTyLqpB5zMgbuti+
+PsEemlbiaCMZFdJk8rJBWADTkirGbXs5ciLwTJyrJXhkBVTqoHtphSWMELxcrnQj
+5bAXZ+rnApWA7LR3yCY1UE0Nj39Yu8lkzDJtbsSXPJVO//OQiGuQRcY6roAUzeS/
+amLdIYqoZm3OF83Ro88XZCIDCG6XjgKoqojtekClVIytB6mHWk+IHhI7DgU/pDBj
+yOskzVRMja9ECnqaVX+bmlmJGZBgcd7+XiebCXNY9IAx/ZQDMbDJDoJusvH6npJR
+PBgag8QBSzwpFyEP/1LrlyVyggZcGuXDWfUiM2rt2UvLm8QYeT5WGfUI+QwW6JRV
+xEz2QwRiPsmj0uK2fXLKHbY0Pv79IMLqgesCAUbndWsi94kjAhpJGa/WfKVb8Dpg
+T+mVa2OT0pgewPWJ3h5f47ag27WDJthsIUHAvNZ03uM23DIqDh6o03/B3/4r1Uab
+HzsLWhUOzbLihO08qcPk/JqtDVCv579mpgvy7bL2ZsZUzefnmg+ah+1bhLguryZu
+Zrt1hTu5WTz27UbYwWNtkxWnWcM9sSF9y+KVwumYhIUGTvg3Jds/jmp/Z5ad8tev
+0RQuWmqnXVD51Y92tOXobJH7JwGbQ4GeRNG/UX7DS9Lvb6rPgmmxvGbXMcjEMapB
+m5DLjCD7Lz4++sfzhCic3/nL8e8fjoYZmCbL/SpYjGjTrfoNaGYaT0De8MxCSjPf
+rNT+ldWFD0oAOTmT0uqOFkeIx3vxczSQUyRfzGOLqtb9mAXk8lvdb8Myb5atfe8f
+FNBL5ocz6Szv2AmKmjZE5P6NKSllyryCYl+H2QU0e3OyY8S9aG2aG8fqeVKaA01S
+uOOjsK6IKMpkit+VFDP7RWvS8p1FTwTVRdbMZzASW8+D98OHsTlOENtQuuFyvNaN
+vPHrbHltTkwDlYVab9UpFr/ZRW3cNJrmcl0hyeCJxk5u8B9UwW6yZmd24hxda2xt
+1QjnxfWlX0i5KTmfWFyPmRYt1WH9rdVaH6wqRbGNbMRT7Lez0E/2+LXjFiiEe0Vh
+ZuryhyhUXpHY8yyHxFY92XT504Z3wPUaJ1tQxGhdziZ7HcqFT8f4oTNJdBNtzqNw
+l63gSTaKUkSB14CBOyR84cqpgSMwZvWZb3RQFYY4GSALWwhnn6PWeRhjWAXPms2v
+tRmfEyDUCyPxDarezvS9kUir3XbvnCkoJOrK/lw4a8ygGBCBqRKOdI0SRUf1Nnjm
+aZBrKrXHv5vP66fyC7cGMTq35+iiHbE//rRDGPrf9vpmw3ki3FlDMyAXOC5yWDco
+0n+cNDyHJDdIW3+3nfZvSgPPMwmTdfNO90X3YczSmMTGjnIuhQMf7FMFmMZOSrqD
+u234vYCjIIlph3aVpbzjOqKg3d4nuP9oouyAgRAr+vShgHRmq0jvsAiGH3Q6RqEj
+DvkM1NgoloMy5RhKF4hIeaQM/UlgoXrcEx0RGCJHykQcQmVa5cf5EwjrO7b+w2Lg
+-----END RSA PRIVATE KEY-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+        Signature Algorithm: md5WithRSAEncryption
+        Issuer: C=US, ST=Berkshire, L=Newbury, O=My Company Ltd, OU=test, CN=test/emailAddress=test@test.com
+        Validity
+            Not Before: Feb  2 02:22:25 2009 GMT
+            Not After : Jan 31 02:22:25 2019 GMT
+        Subject: C=US, ST=Berkshire, O=My Company Ltd, OU=test-client, CN=test-client/emailAddress=test-client@test.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (2048 bit)
+                Modulus (2048 bit):
+                    00:ce:d2:e9:fb:f4:a4:ce:66:87:cc:8b:2d:d2:c3:
+                    b8:fa:be:ea:53:08:95:9e:27:4a:04:78:31:a1:36:
+                    d2:e5:42:f5:76:f7:24:71:b8:d3:10:fd:b7:57:86:
+                    68:29:02:1b:e9:93:2b:af:c0:7e:3d:58:16:34:10:
+                    25:85:7e:28:2d:42:fc:34:7f:d6:4f:0a:e7:b6:b9:
+                    40:51:c8:e5:89:60:cd:73:51:63:67:e1:59:36:31:
+                    5e:d0:87:4b:5d:97:8a:42:22:d3:42:f2:83:62:79:
+                    84:45:5b:d8:20:97:e2:d1:34:23:5e:ec:bf:8a:41:
+                    7c:ad:80:50:f3:9b:1c:51:5d:e4:29:1c:c1:c6:7c:
+                    a1:00:70:0f:f6:79:77:03:22:f5:4b:ab:76:ca:20:
+                    ab:e1:4e:53:6c:86:68:e9:d6:0f:6e:95:4b:b5:b5:
+                    cf:d3:6c:d4:73:db:c0:3d:1d:20:20:72:ab:3e:25:
+                    bb:3f:cd:5f:2d:77:fd:d7:0b:a8:eb:0c:73:c0:3f:
+                    74:27:17:56:cf:31:9a:a9:ed:c1:39:27:c7:aa:e9:
+                    77:48:f4:6d:56:89:35:5c:79:5b:f6:2f:8c:9f:93:
+                    bc:13:1c:d0:74:5f:30:1b:15:43:71:8e:ff:1a:df:
+                    65:65:69:c2:8a:5a:ab:04:4a:80:de:20:29:8d:48:
+                    1c:83
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                34:E9:5C:D8:FC:C7:AF:F7:5D:F4:13:D3:82:1A:CD:52:0F:4A:BB:0B
+            X509v3 Authority Key Identifier: 
+                keyid:52:33:2E:22:66:79:82:E5:41:2E:8D:3B:B2:E5:CC:FA:BE:99:33:8E
+                DirName:/C=US/ST=Berkshire/L=Newbury/O=My Company Ltd/OU=test/CN=test/emailAddress=test@test.com
+                serial:E6:4C:5C:96:26:5E:D6:D0
+
+    Signature Algorithm: md5WithRSAEncryption
+        88:c4:dd:c5:4a:a6:8e:1f:36:62:80:31:6b:2b:85:34:0d:6c:
+        a7:dc:e1:5a:3c:dd:74:57:db:59:be:8b:f2:79:40:2d:bd:9b:
+        e1:ab:0e:03:75:20:75:2f:ea:97:f9:d4:7e:15:75:c2:82:f5:
+        b1:37:e4:8b:24:d4:ef:02:3b:19:6a:56:bb:9f:c3:a5:b4:c4:
+        39:a0:64:96:5b:c3:a8:19:74:ec:0b:cf:33:df:f3:12:f3:e2:
+        ab:1d:7d:de:21:64:cd:b1:a0:6f:7a:77:84:d4:62:8a:50:e6:
+        76:5d:af:3a:ef:1a:87:0e:1a:38:c4:f9:0d:2f:14:e0:20:32:
+        4c:2e:70:d2:71:82:d5:e5:c9:13:2c:4e:b4:c8:63:65:1d:48:
+        e8:c3:56:6d:88:84:57:65:13:1e:42:48:b4:03:25:71:4e:12:
+        9d:1a:88:65:d5:71:21:3b:0e:be:37:1c:f0:88:6b:45:0c:0e:
+        6c:e5:60:cb:86:bf:bf:40:30:c4:cf:94:f8:1c:60:d4:1b:df:
+        88:02:7a:45:7e:d1:c6:f3:07:b9:b1:8f:2a:55:2f:be:77:9f:
+        d1:46:64:05:b7:67:c5:b2:ac:8c:42:8f:6a:51:a4:0d:2b:16:
+        a4:d6:39:a5:f8:25:ed:68:25:13:b5:19:ac:6c:e9:ec:ee:03:
+        6b:80:38:6e
+-----BEGIN CERTIFICATE-----
+MIIErjCCA5agAwIBAgIBATANBgkqhkiG9w0BAQQFADCBiDELMAkGA1UEBhMCVVMx
+EjAQBgNVBAgTCUJlcmtzaGlyZTEQMA4GA1UEBxMHTmV3YnVyeTEXMBUGA1UEChMO
+TXkgQ29tcGFueSBMdGQxDTALBgNVBAsTBHRlc3QxDTALBgNVBAMTBHRlc3QxHDAa
+BgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMDkwMjAyMDIyMjI1WhcNMTkw
+MTMxMDIyMjI1WjCBizELMAkGA1UEBhMCVVMxEjAQBgNVBAgTCUJlcmtzaGlyZTEX
+MBUGA1UEChMOTXkgQ29tcGFueSBMdGQxFDASBgNVBAsTC3Rlc3QtY2xpZW50MRQw
+EgYDVQQDEwt0ZXN0LWNsaWVudDEjMCEGCSqGSIb3DQEJARYUdGVzdC1jbGllbnRA
+dGVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDO0un79KTO
+ZofMiy3Sw7j6vupTCJWeJ0oEeDGhNtLlQvV29yRxuNMQ/bdXhmgpAhvpkyuvwH49
+WBY0ECWFfigtQvw0f9ZPCue2uUBRyOWJYM1zUWNn4Vk2MV7Qh0tdl4pCItNC8oNi
+eYRFW9ggl+LRNCNe7L+KQXytgFDzmxxRXeQpHMHGfKEAcA/2eXcDIvVLq3bKIKvh
+TlNshmjp1g9ulUu1tc/TbNRz28A9HSAgcqs+Jbs/zV8td/3XC6jrDHPAP3QnF1bP
+MZqp7cE5J8eq6XdI9G1WiTVceVv2L4yfk7wTHNB0XzAbFUNxjv8a32VlacKKWqsE
+SoDeICmNSByDAgMBAAGjggEcMIIBGDAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf
+Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNOlc2PzH
+r/dd9BPTghrNUg9Kuwswgb0GA1UdIwSBtTCBsoAUUjMuImZ5guVBLo07suXM+r6Z
+M46hgY6kgYswgYgxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAO
+BgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMQ0wCwYDVQQL
+EwR0ZXN0MQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3Qu
+Y29tggkA5kxcliZe1tAwDQYJKoZIhvcNAQEEBQADggEBAIjE3cVKpo4fNmKAMWsr
+hTQNbKfc4Vo83XRX21m+i/J5QC29m+GrDgN1IHUv6pf51H4VdcKC9bE35Isk1O8C
+OxlqVrufw6W0xDmgZJZbw6gZdOwLzzPf8xLz4qsdfd4hZM2xoG96d4TUYopQ5nZd
+rzrvGocOGjjE+Q0vFOAgMkwucNJxgtXlyRMsTrTIY2UdSOjDVm2IhFdlEx5CSLQD
+JXFOEp0aiGXVcSE7Dr43HPCIa0UMDmzlYMuGv79AMMTPlPgcYNQb34gCekV+0cbz
+B7mxjypVL753n9FGZAW3Z8WyrIxCj2pRpA0rFqTWOaX4Je1oJRO1Gaxs6ezuA2uA
+OG4=
+-----END CERTIFICATE-----
diff --git a/libnm-util/tests/test-crypto.c b/libnm-util/tests/test-crypto.c
new file mode 100644
index 00000000..a5466bc8
--- /dev/null
+++ b/libnm-util/tests/test-crypto.c
@@ -0,0 +1,317 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2009 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nm-test-helpers.h"
+#include "crypto.h"
+#include "nm-utils.h"
+
+#if 0
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_rsa_key_end = "-----END RSA PRIVATE KEY-----";
+
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_dsa_key_end = "-----END DSA PRIVATE KEY-----";
+
+static void
+dump_key_to_pem (const char *key, gsize key_len, int key_type)
+{
+	char *b64 = NULL;
+	GString *str = NULL;
+	const char *start_tag;
+	const char *end_tag;
+	char *p;
+
+	switch (key_type) {
+	case NM_CRYPTO_KEY_TYPE_RSA:
+		start_tag = pem_rsa_key_begin;
+		end_tag = pem_rsa_key_end;
+		break;
+	case NM_CRYPTO_KEY_TYPE_DSA:
+		start_tag = pem_dsa_key_begin;
+		end_tag = pem_dsa_key_end;
+		break;
+	default:
+		g_warning ("Unknown key type %d", key_type);
+		return;
+	}
+
+	b64 = g_base64_encode ((const unsigned char *) key, key_len);
+	if (!b64) {
+		g_warning ("Couldn't base64 encode the key.");
+		goto out;
+	}
+
+	str = g_string_new (NULL);
+	if (!str) {
+		g_warning ("Couldn't allocate buffer to write out key.");
+		goto out;
+	}
+
+	g_string_append (str, start_tag);
+	g_string_append_c (str, '\n');
+
+	for (p = b64; p < (b64 + strlen (b64)); p += 64) {
+		g_string_append_len (str, p, strnlen (p, 64));
+		g_string_append_c (str, '\n');
+	}
+
+	g_string_append (str, end_tag);
+	g_string_append_c (str, '\n');
+
+	g_message ("Decrypted private key:\n\n%s", str->str);
+
+out:
+	g_free (b64);
+	if (str)
+		g_string_free (str, TRUE);
+}
+#endif
+
+static void
+test_load_cert (const char *path, const char *desc)
+{
+	GByteArray *array;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GError *error = NULL;
+
+	array = crypto_load_and_verify_certificate (path, &format, &error);
+	ASSERT (array != NULL, desc,
+	        "couldn't read certificate file '%s': %d %s",
+	        path, error->code, error->message);
+
+	ASSERT (format == NM_CRYPTO_FILE_FORMAT_X509, desc,
+	        "%s: unexpected certificate format (expected %d, got %d)",
+	        path, NM_CRYPTO_FILE_FORMAT_X509, format);
+
+	g_byte_array_free (array, TRUE);
+}
+
+static void
+test_load_private_key (const char *path,
+                       const char *password,
+                       gboolean expect_fail,
+                       const char *desc)
+{
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *array;
+	GError *error = NULL;
+
+	array = crypto_get_private_key (path, password, &key_type, &format, &error);
+	if (expect_fail) {
+		ASSERT (array == NULL, desc,
+		        "unexpected success reading private key file '%s' with "
+		        "invalid password",
+		        path);
+
+		ASSERT (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, desc,
+		        "unexpected success determining private key file '%s' "
+		        "format with invalid password (expected %d, got %d)",
+		        path, NM_CRYPTO_FILE_FORMAT_UNKNOWN, format);
+		return;
+	}
+
+	ASSERT (array != NULL, desc,
+	        "couldn't read private key file '%s': %d %s",
+	        path, error->code, error->message);
+
+	ASSERT (format == NM_CRYPTO_FILE_FORMAT_RAW_KEY, desc,
+	        "%s: unexpected private key file format (expected %d, got %d)",
+	        path, NM_CRYPTO_FILE_FORMAT_RAW_KEY, format);
+
+	ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc,
+	        "%s: unexpected private key type (expected %d, got %d)",
+	        path, NM_CRYPTO_KEY_TYPE_RSA, format);
+
+	g_byte_array_free (array, TRUE);
+}
+
+static void
+test_load_pkcs12 (const char *path,
+                  const char *password,
+                  gboolean expect_fail,
+                  const char *desc)
+{
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *array;
+	GError *error = NULL;
+
+	array = crypto_get_private_key (path, password, &key_type, &format, &error);
+	if (expect_fail) {
+		ASSERT (array == NULL, desc,
+		        "unexpected success reading PKCS#12 private key file "
+		        "'%s' with invalid password",
+		        path);
+
+		/* PKCS#12 file format can be determined even if the password
+		 * is wrong; check that.
+		 */
+		ASSERT (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, desc,
+		        "unexpected success determining PKCS#12 private key "
+		        "'%s' file format with invalid password (expected %d, "
+		        "got %d)",
+		        path, NM_CRYPTO_FILE_FORMAT_UNKNOWN, format);
+		ASSERT (key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, desc,
+		        "unexpected success determining PKCS#12 private key "
+		        "'%s' type with invalid password (expected %d, got %d)",
+		        path, NM_CRYPTO_KEY_TYPE_UNKNOWN, key_type);
+		return;
+	}
+
+	ASSERT (array != NULL, desc,
+	        "couldn't read PKCS#12 private key file '%s': %d %s",
+	        path, error->code, error->message);
+
+	ASSERT (format == NM_CRYPTO_FILE_FORMAT_PKCS12, desc,
+	        "%s: unexpected PKCS#12 private key file format (expected %d, got %d)",
+	        path, NM_CRYPTO_FILE_FORMAT_RAW_KEY, format);
+
+	ASSERT (key_type == NM_CRYPTO_KEY_TYPE_ENCRYPTED, desc,
+	        "%s: unexpected PKCS#12 private key type (expected %d, got %d)",
+	        path, NM_CRYPTO_KEY_TYPE_ENCRYPTED, format);
+
+	g_byte_array_free (array, TRUE);
+}
+
+static void
+test_is_pkcs12 (const char *path, gboolean expect_fail, const char *desc)
+{
+	gboolean is_pkcs12;
+
+	is_pkcs12 = crypto_is_pkcs12_file (path, NULL);
+	if (expect_fail) {
+		ASSERT (is_pkcs12 == FALSE, desc,
+		        "unexpected success reading non-PKCS#12 file '%s'",
+		        path);
+		return;
+	}
+
+	ASSERT (is_pkcs12 == TRUE, desc, "couldn't read PKCS#12 file '%s'", path);
+}
+
+static void
+test_encrypt_private_key (const char *path,
+                          const char *password,
+                          const char *desc)
+{
+	NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	GByteArray *array, *encrypted, *re_decrypted;
+	GError *error = NULL;
+
+	array = crypto_get_private_key (path, password, &key_type, &format, &error);
+
+	ASSERT (array != NULL, desc,
+	        "couldn't read private key file '%s': %d %s",
+	        path, error->code, error->message);
+
+	ASSERT (format == NM_CRYPTO_FILE_FORMAT_RAW_KEY, desc,
+	        "%s: unexpected private key file format (expected %d, got %d)",
+	        path, NM_CRYPTO_FILE_FORMAT_RAW_KEY, format);
+
+	ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc,
+	        "%s: unexpected private key type (expected %d, got %d)",
+	        path, NM_CRYPTO_KEY_TYPE_RSA, format);
+
+	/* Now re-encrypt the private key */
+	encrypted = nm_utils_rsa_key_encrypt (array, password, NULL, &error);
+	ASSERT (encrypted != NULL, desc,
+	        "couldn't re-encrypt private key file '%s': %d %s",
+	        path, error->code, error->message);
+
+	/* Then re-decrypt the private key */
+	key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+	format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+	re_decrypted = crypto_get_private_key_data (encrypted, password, &key_type, &format, &error);
+
+	ASSERT (re_decrypted != NULL, desc,
+	        "couldn't read private key file '%s': %d %s",
+	        path, error->code, error->message);
+
+	ASSERT (format == NM_CRYPTO_FILE_FORMAT_RAW_KEY, desc,
+	        "%s: unexpected private key file format (expected %d, got %d)",
+	        path, NM_CRYPTO_FILE_FORMAT_RAW_KEY, format);
+
+	ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc,
+	        "%s: unexpected private key type (expected %d, got %d)",
+	        path, NM_CRYPTO_KEY_TYPE_RSA, format);
+
+	/* Compare the original decrypted key with the re-decrypted key */
+	ASSERT (array->len == re_decrypted->len, desc,
+	        "%s: unexpected re-decrypted private key length (expected %d, got %d)",
+	        path, array->len, re_decrypted->len);
+
+	ASSERT (!memcmp (array->data, re_decrypted->data, array->len), desc,
+	        "%s: unexpected private key data",
+	        path);
+
+	g_byte_array_free (re_decrypted, TRUE);
+	g_byte_array_free (encrypted, TRUE);
+	g_byte_array_free (array, TRUE);
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	char *progname;
+
+	ASSERT (argc > 2, "test-crypto",
+	        "wrong number of arguments (expected at least an operation and an object)");
+
+	if (!crypto_init (&error))
+		FAIL ("crypto-init", "failed to initialize crypto: %s", error->message);
+
+	if (!strcmp (argv[1], "--cert"))
+		test_load_cert (argv[2], "cert");
+	else if (!strcmp (argv[1], "--key")) {
+		ASSERT (argc == 4, "test-crypto",
+		        "wrong number of arguments (--key <key file> <password>)");
+
+		test_load_private_key (argv[2], argv[3], FALSE, "private-key");
+		test_load_private_key (argv[2], "blahblahblah", TRUE, "private-key-bad-password");
+		test_encrypt_private_key (argv[2], argv[3], "private-key-rencrypt");
+		test_is_pkcs12 (argv[2], TRUE, "is-pkcs12-not-pkcs12");
+	} else if (!strcmp (argv[1], "--p12")) {
+		test_is_pkcs12 (argv[2], FALSE, "is-pkcs12");
+		test_load_pkcs12 (argv[2], argv[3], FALSE, "pkcs12-private-key");
+		test_load_pkcs12 (argv[2], "blahblahblah", TRUE, "pkcs12-private-key-bad-password");
+	} else {
+		ASSERT (argc > 2, "test-crypto", "unknown test type (not --cert, --key, or --p12)");
+	}
+
+	crypto_deinit ();
+
+	progname = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", progname);
+	g_free (progname);
+	return 0;
+}
+
diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c
new file mode 100644
index 00000000..95304459
--- /dev/null
+++ b/libnm-util/tests/test-general.c
@@ -0,0 +1,246 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+
+#include "nm-test-helpers.h"
+#include <nm-utils.h>
+
+#include "nm-setting-connection.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-dbus-glib-types.h"
+
+static void
+vpn_check_func (const char *key, const char *value, gpointer user_data)
+{
+	const char *test = user_data;
+
+	if (!strcmp (key, "foobar1")) {
+		ASSERT (strcmp (value, "blahblah1") == 0,
+				test, "unexpected vpn item '%s' / '%s'", key, value);
+		return;
+	}
+
+	if (!strcmp (key, "foobar2")) {
+		ASSERT (strcmp (value, "blahblah2") == 0,
+				test, "unexpected vpn item '%s' / '%s'", key, value);
+		return;
+	}
+
+	if (!strcmp (key, "foobar3")) {
+		ASSERT (strcmp (value, "blahblah3") == 0,
+				test, "unexpected vpn item '%s' / '%s'", key, value);
+		return;
+	}
+
+	if (!strcmp (key, "foobar4")) {
+		ASSERT (strcmp (value, "blahblah4") == 0,
+				test, "unexpected vpn item '%s' / '%s'", key, value);
+		return;
+	}
+
+	ASSERT (FALSE, test, "unexpected vpn item '%s'", key);
+}
+
+static void
+vpn_check_empty_func (const char *key, const char *value, gpointer user_data)
+{
+	const char *test = user_data;
+
+	/* We don't expect any values */
+	ASSERT (FALSE, test, "unexpected vpn item '%s'", key);
+}
+
+static void
+test_setting_vpn_items (void)
+{
+	NMSettingVPN *s_vpn;
+
+	s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
+	ASSERT (s_vpn != NULL,
+	        "vpn-items",
+	        "error creating vpn setting");
+
+	nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1");
+	nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2");
+	nm_setting_vpn_add_data_item (s_vpn, "foobar3", "blahblah3");
+	nm_setting_vpn_add_data_item (s_vpn, "foobar4", "blahblah4");
+
+	/* Ensure that added values are all present */
+	nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_func, "vpn-data");
+	nm_setting_vpn_remove_data_item (s_vpn, "foobar1");
+	nm_setting_vpn_remove_data_item (s_vpn, "foobar2");
+	nm_setting_vpn_remove_data_item (s_vpn, "foobar3");
+	nm_setting_vpn_remove_data_item (s_vpn, "foobar4");
+
+	nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1");
+	nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2");
+	nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3");
+	nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4");
+
+	/* Ensure that added values are all present */
+	nm_setting_vpn_foreach_secret (s_vpn, vpn_check_func, "vpn-secrets");
+	nm_setting_vpn_remove_secret (s_vpn, "foobar1");
+	nm_setting_vpn_remove_secret (s_vpn, "foobar2");
+	nm_setting_vpn_remove_secret (s_vpn, "foobar3");
+	nm_setting_vpn_remove_secret (s_vpn, "foobar4");
+
+	/* Try to add some blank values and make sure they are rejected */
+	nm_setting_vpn_add_data_item (s_vpn, NULL, NULL);
+	nm_setting_vpn_add_data_item (s_vpn, "", "");
+	nm_setting_vpn_add_data_item (s_vpn, "foobar1", NULL);
+	nm_setting_vpn_add_data_item (s_vpn, "foobar1", "");
+	nm_setting_vpn_add_data_item (s_vpn, NULL, "blahblah1");
+	nm_setting_vpn_add_data_item (s_vpn, "", "blahblah1");
+
+	nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_empty_func, "vpn-data-empty");
+
+	/* Try to add some blank secrets and make sure they are rejected */
+	nm_setting_vpn_add_secret (s_vpn, NULL, NULL);
+	nm_setting_vpn_add_secret (s_vpn, "", "");
+	nm_setting_vpn_add_secret (s_vpn, "foobar1", NULL);
+	nm_setting_vpn_add_secret (s_vpn, "foobar1", "");
+	nm_setting_vpn_add_secret (s_vpn, NULL, "blahblah1");
+	nm_setting_vpn_add_secret (s_vpn, "", "blahblah1");
+
+	nm_setting_vpn_foreach_secret (s_vpn, vpn_check_empty_func, "vpn-secrets-empty");
+
+	g_object_unref (s_vpn);
+}
+
+#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
+#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS))
+
+/* Test that setting the IPv6 setting's 'addresses' property using the old
+ * IPv6 address format still works, i.e. that the GValue transformation function
+ * from old->new is working correctly.
+ */
+static void
+test_setting_ip6_config_old_address_array (void)
+{
+	NMSettingIP6Config *s_ip6;
+	GPtrArray *addresses, *read_addresses;
+	GValueArray *array, *read_array;
+	GValue element = {0, }, written_value = {0, }, read_value = {0, };
+	GByteArray *ba;
+	const guint8 addr[16] = { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
+	                          0x11, 0x22, 0x33, 0x44, 0x66, 0x77, 0x88, 0x99 };
+	const guint8 gw[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	guint32 prefix = 56;
+	GValue *read_addr, *read_prefix, *read_gw;
+
+	s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
+	ASSERT (s_ip6 != NULL,
+	        "ip6-old-addr", "error creating IP6 setting");
+
+	g_value_init (&written_value, OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS);
+
+	addresses = g_ptr_array_new ();
+	array = g_value_array_new (3);
+
+	/* IP address */
+	g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+	ba = g_byte_array_new ();
+	g_byte_array_append (ba, &addr[0], sizeof (addr));
+	g_value_take_boxed (&element, ba);
+	g_value_array_append (array, &element);
+	g_value_unset (&element);
+
+	/* Prefix */
+	g_value_init (&element, G_TYPE_UINT);
+	g_value_set_uint (&element, prefix);
+	g_value_array_append (array, &element);
+	g_value_unset (&element);
+
+	g_ptr_array_add (addresses, array);
+	g_value_set_boxed (&written_value, addresses);
+
+	/* Set the address array on the object */
+	g_object_set_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &written_value);
+
+	/* Get it back so we can compare it */
+	g_value_init (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS);
+	g_object_get_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &read_value);
+
+	ASSERT (G_VALUE_HOLDS (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS),
+	        "ip6-old-addr", "wrong addresses property value type '%s'",
+	        G_VALUE_TYPE_NAME (&read_value));
+
+	read_addresses = (GPtrArray *) g_value_get_boxed (&read_value);
+	ASSERT (read_addresses != NULL,
+	        "ip6-old-addr", "missing addresses on readback");
+	ASSERT (read_addresses->len == 1,
+	        "ip6-old-addr", "expected one address on readback");
+
+	read_array = (GValueArray *) g_ptr_array_index (read_addresses, 0);
+
+	read_addr = g_value_array_get_nth (read_array, 0);
+	ba = g_value_get_boxed (read_addr);
+	ASSERT (ba->len == sizeof (addr),
+	        "ip6-old-addr", "unexpected address item length %d", ba->len);
+	ASSERT (memcmp (ba->data, &addr[0], sizeof (addr)) == 0,
+	        "ip6-old-addr", "unexpected failure comparing addresses");
+
+	read_prefix = g_value_array_get_nth (read_array, 1);
+	ASSERT (g_value_get_uint (read_prefix) == prefix,
+	        "ip6-old-addr", "unexpected failure comparing prefix");
+
+	/* Ensure the gateway is all zeros, which is how the 2-item to 3-item
+	 * conversion happens.
+	 */
+	read_gw = g_value_array_get_nth (read_array, 2);
+	ba = g_value_get_boxed (read_gw);
+	ASSERT (ba->len == sizeof (gw),
+	        "ip6-old-addr", "unexpected gateway item length %d", ba->len);
+	ASSERT (memcmp (ba->data, &gw[0], sizeof (gw)) == 0,
+	        "ip6-old-addr", "unexpected failure comparing gateways");
+
+	g_value_unset (&written_value);
+	g_value_unset (&read_value);
+	g_object_unref (s_ip6);
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	DBusGConnection *bus;
+	char *base;
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	if (!nm_utils_init (&error))
+		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+	/* The tests */
+	test_setting_vpn_items ();
+	test_setting_ip6_config_old_address_array ();
+
+	base = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", base);
+	g_free (base);
+	return 0;
+}
+
diff --git a/libnm-util/tests/test-need-secrets.c b/libnm-util/tests/test-need-secrets.c
new file mode 100644
index 00000000..517e2e01
--- /dev/null
+++ b/libnm-util/tests/test-need-secrets.c
@@ -0,0 +1,566 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2009 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+
+#include "nm-test-helpers.h"
+#include <nm-utils.h>
+
+#include "nm-setting-connection.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-8021x.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-vpn.h"
+
+
+#define TEST_NEED_SECRETS_EAP_TLS_CA_CERT TEST_CERT_DIR "/test_ca_cert.pem"
+#define TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT TEST_CERT_DIR "/test_key_and_cert.pem"
+#define TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY TEST_CERT_DIR "/test_key_and_cert.pem"
+
+static gboolean
+find_hints_item (GPtrArray *hints, const char *item)
+{
+	int i;
+
+	for (i = 0; i < hints->len; i++) {
+		if (!strcmp (item, (const char *) g_ptr_array_index (hints, i)))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static NMConnection *
+make_tls_connection (const char *detail, NMSetting8021xCKScheme scheme)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSetting8021x *s_8021x;
+	NMSettingWired *s_wired;
+	NMSettingIP4Config *s_ip4;
+	char *uuid;
+	gboolean success;
+	GError *error = NULL;
+
+	connection = nm_connection_new ();
+	ASSERT (connection != NULL,
+	        detail, "failed to allocate new connection");
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	ASSERT (s_con != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_CONNECTION_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Need TLS Secrets",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	ASSERT (s_wired != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_WIRED_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	/* Wireless security setting */
+	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+	ASSERT (s_8021x != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_802_1X_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+
+	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+
+	nm_setting_802_1x_add_eap_method (s_8021x, "tls");
+
+	success = nm_setting_802_1x_set_ca_cert (s_8021x,
+	                                         TEST_NEED_SECRETS_EAP_TLS_CA_CERT,
+	                                         scheme,
+	                                         NULL,
+	                                         &error);
+	ASSERT (success == TRUE,
+	        detail, "failed to set CA certificate '%s': %s",
+	        TEST_NEED_SECRETS_EAP_TLS_CA_CERT, error->message);
+
+	success = nm_setting_802_1x_set_client_cert (s_8021x,
+	                                             TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT,
+	                                             scheme,
+	                                             NULL,
+	                                             &error);
+	ASSERT (success == TRUE,
+	        detail, "failed to set client certificate '%s': %s",
+	        TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, error->message);
+
+	success = nm_setting_802_1x_set_private_key (s_8021x,
+	                                             TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY,
+	                                             "test",
+	                                             scheme,
+	                                             NULL,
+	                                             &error);
+	ASSERT (success == TRUE,
+	        detail, "failed to set private key '%s': %s",
+	        TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, error->message);
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	ASSERT (s_ip4 != NULL,
+			detail, "failed to allocate new %s setting",
+			NM_SETTING_IP4_CONFIG_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	ASSERT (nm_connection_verify (connection, &error) == TRUE,
+	        detail, "failed to verify connection: %s",
+	        (error && error->message) ? error->message : "(unknown)");
+
+	return connection;
+}
+
+static void
+test_need_tls_secrets_path (void)
+{
+	NMConnection *connection;
+	const char *setting_name;
+	GPtrArray *hints = NULL;
+	NMSetting8021x *s_8021x;
+
+	connection = make_tls_connection ("need-tls-secrets-path-key", NM_SETTING_802_1X_CK_SCHEME_PATH);
+	ASSERT (connection != NULL,
+	        "need-tls-secrets-path-key",
+	        "error creating test connection");
+
+	/* Ensure we don't need any secrets since we just set up the connection */
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name == NULL,
+	        "need-tls-secrets-path-key",
+	        "secrets are unexpectedly required");
+	ASSERT (hints == NULL,
+	        "need-tls-secrets-path-key",
+	        "hints should be NULL since no secrets were required");
+
+	/* Connection is good; clear secrets and ensure private key is then required */
+	nm_connection_clear_secrets (connection);
+
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name != NULL,
+	        "need-tls-secrets-path-key",
+	        "unexpected secrets success");
+	ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+			"need-tls-secrets-path-key",
+			"unexpected setting secrets required");
+
+	ASSERT (hints != NULL,
+	        "need-tls-secrets-path-key",
+	        "expected returned secrets hints");
+	ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY),
+			"need-tls-secrets-path-key",
+			"expected to require private key, but it wasn't");
+
+	g_object_unref (connection);
+
+	/*** Just clear the private key this time ***/
+
+	connection = make_tls_connection ("need-tls-secrets-path-key-password", NM_SETTING_802_1X_CK_SCHEME_PATH);
+	ASSERT (connection != NULL,
+	        "need-tls-secrets-path-key-password",
+	        "error creating test connection");
+
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	ASSERT (s_8021x != NULL,
+	        "need-tls-secrets-path-key-password",
+	        "error getting test 802.1x setting");
+
+	g_object_set (G_OBJECT (s_8021x), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, NULL, NULL);
+
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name != NULL,
+	        "need-tls-secrets-path-key-password",
+	        "unexpected secrets success");
+	ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+			"need-tls-secrets-path-key-password",
+			"unexpected setting secrets required");
+
+	ASSERT (hints != NULL,
+	        "need-tls-secrets-path-key-password",
+	        "expected returned secrets hints");
+	ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD),
+			"need-tls-secrets-path-key-password",
+			"expected to require private key password, but it wasn't");
+
+	g_object_unref (connection);
+}
+
+static void
+test_need_tls_secrets_blob (void)
+{
+	NMConnection *connection;
+	const char *setting_name;
+	GPtrArray *hints = NULL;
+	NMSetting8021x *s_8021x;
+
+	connection = make_tls_connection ("need-tls-secrets-blob-key", NM_SETTING_802_1X_CK_SCHEME_BLOB);
+	ASSERT (connection != NULL,
+	        "need-tls-secrets-blob-key",
+	        "error creating test connection");
+
+	/* Ensure we don't need any secrets since we just set up the connection */
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name == NULL,
+	        "need-tls-secrets-blob-key",
+	        "secrets are unexpectedly required");
+	ASSERT (hints == NULL,
+	        "need-tls-secrets-blob-key",
+	        "hints should be NULL since no secrets were required");
+
+	/* Connection is good; clear secrets and ensure private key is then required */
+	nm_connection_clear_secrets (connection);
+
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name != NULL,
+	        "need-tls-secrets-blob-key",
+	        "unexpected secrets success");
+	ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+			"need-tls-secrets-blob-key",
+			"unexpected setting secrets required");
+
+	ASSERT (hints != NULL,
+	        "need-tls-secrets-blob-key",
+	        "expected returned secrets hints");
+	ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY),
+			"need-tls-secrets-blob-key",
+			"expected to require private key, but it wasn't");
+
+	g_object_unref (connection);
+
+	/*** Just clear the private key this time ***/
+
+	connection = make_tls_connection ("need-tls-secrets-blob-key-password", NM_SETTING_802_1X_CK_SCHEME_BLOB);
+	ASSERT (connection != NULL,
+	        "need-tls-secrets-blob-key-password",
+	        "error creating test connection");
+
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	ASSERT (s_8021x != NULL,
+	        "need-tls-secrets-blob-key-password",
+	        "error getting test 802.1x setting");
+
+	g_object_set (G_OBJECT (s_8021x), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, NULL, NULL);
+
+	/* Blobs are already decrypted and don't need a password */
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name == NULL,
+	        "need-tls-secrets-blob-key-password",
+	        "unexpected secrets failure");
+	ASSERT (hints == NULL,
+	        "need-tls-secrets-blob-key-password",
+	        "hints should be NULL since no secrets were required");
+
+	g_object_unref (connection);
+}
+
+static NMConnection *
+make_tls_phase2_connection (const char *detail, NMSetting8021xCKScheme scheme)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSetting8021x *s_8021x;
+	NMSettingWired *s_wired;
+	NMSettingIP4Config *s_ip4;
+	char *uuid;
+	gboolean success;
+	GError *error = NULL;
+
+	connection = nm_connection_new ();
+	ASSERT (connection != NULL,
+	        detail, "failed to allocate new connection");
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	ASSERT (s_con != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_CONNECTION_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Need TLS Secrets",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	ASSERT (s_wired != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_WIRED_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	/* Wireless security setting */
+	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+	ASSERT (s_8021x != NULL,
+	        detail, "failed to allocate new %s setting",
+	        NM_SETTING_802_1X_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+
+	g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "blahblah", NULL);
+	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+
+	nm_setting_802_1x_add_eap_method (s_8021x, "ttls");
+	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, "tls", NULL);
+
+	success = nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
+	                                                TEST_NEED_SECRETS_EAP_TLS_CA_CERT,
+	                                                scheme,
+	                                                NULL,
+	                                                &error);
+	ASSERT (success == TRUE,
+	        detail, "failed to set phase2 CA certificate '%s': %s",
+	        TEST_NEED_SECRETS_EAP_TLS_CA_CERT, error->message);
+
+	success = nm_setting_802_1x_set_phase2_client_cert (s_8021x,
+	                                                    TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT,
+	                                                    scheme,
+	                                                    NULL,
+	                                                    &error);
+	ASSERT (success == TRUE,
+	        detail, "failed to set phase2 client certificate '%s': %s",
+	        TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, error->message);
+
+	success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+	                                                    TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY,
+	                                                    "test",
+	                                                    scheme,
+	                                                    NULL,
+	                                                    &error);
+	ASSERT (success == TRUE,
+	        detail, "failed to set phase2 private key '%s': %s",
+	        TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, error->message);
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	ASSERT (s_ip4 != NULL,
+			detail, "failed to allocate new %s setting",
+			NM_SETTING_IP4_CONFIG_SETTING_NAME);
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+	ASSERT (nm_connection_verify (connection, &error) == TRUE,
+	        detail, "failed to verify connection: %s",
+	        (error && error->message) ? error->message : "(unknown)");
+
+	return connection;
+}
+
+static void
+test_need_tls_phase2_secrets_path (void)
+{
+	NMConnection *connection;
+	const char *setting_name;
+	GPtrArray *hints = NULL;
+	NMSetting8021x *s_8021x;
+
+	connection = make_tls_phase2_connection ("need-tls-phase2-secrets-path-key",
+	                                         NM_SETTING_802_1X_CK_SCHEME_PATH);
+	ASSERT (connection != NULL,
+	        "need-tls-phase2-secrets-path-key",
+	        "error creating test connection");
+
+	/* Ensure we don't need any secrets since we just set up the connection */
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name == NULL,
+	        "need-tls-phase2-secrets-path-key",
+	        "secrets are unexpectedly required");
+	ASSERT (hints == NULL,
+	        "need-tls-phase2-secrets-path-key",
+	        "hints should be NULL since no secrets were required");
+
+	/* Connection is good; clear secrets and ensure private key is then required */
+	nm_connection_clear_secrets (connection);
+
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name != NULL,
+	        "need-tls-phase2-secrets-path-key",
+	        "unexpected secrets success");
+	ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+			"need-tls-phase2-secrets-path-key",
+			"unexpected setting secrets required");
+
+	ASSERT (hints != NULL,
+	        "need-tls-phase2-secrets-path-key",
+	        "expected returned secrets hints");
+	ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY),
+			"need-tls-phase2-secrets-path-key",
+			"expected to require private key, but it wasn't");
+
+	g_object_unref (connection);
+
+	/*** Just clear the private key this time ***/
+
+	connection = make_tls_phase2_connection ("need-tls-phase2-secrets-path-key-password",
+	                                         NM_SETTING_802_1X_CK_SCHEME_PATH);
+	ASSERT (connection != NULL,
+	        "need-tls-phase2-secrets-path-key-password",
+	        "error creating test connection");
+
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	ASSERT (s_8021x != NULL,
+	        "need-tls-phase2-secrets-path-key-password",
+	        "error getting test 802.1x setting");
+
+	g_object_set (G_OBJECT (s_8021x), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, NULL, NULL);
+
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name != NULL,
+	        "need-tls-phase2-secrets-path-key-password",
+	        "unexpected secrets success");
+	ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+			"need-tls-phase2-secrets-path-key-password",
+			"unexpected setting secrets required");
+
+	ASSERT (hints != NULL,
+	        "need-tls-phase2-secrets-path-key-password",
+	        "expected returned secrets hints");
+	ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD),
+			"need-tls-phase2-secrets-path-key-password",
+			"expected to require private key password, but it wasn't");
+
+	g_object_unref (connection);
+}
+
+static void
+test_need_tls_phase2_secrets_blob (void)
+{
+	NMConnection *connection;
+	const char *setting_name;
+	GPtrArray *hints = NULL;
+	NMSetting8021x *s_8021x;
+
+	connection = make_tls_phase2_connection ("need-tls-phase2-secrets-blob-key",
+	                                         NM_SETTING_802_1X_CK_SCHEME_BLOB);
+	ASSERT (connection != NULL,
+	        "need-tls-phase2-secrets-blob-key",
+	        "error creating test connection");
+
+	/* Ensure we don't need any secrets since we just set up the connection */
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name == NULL,
+	        "need-tls-phase2-secrets-blob-key",
+	        "secrets are unexpectedly required");
+	ASSERT (hints == NULL,
+	        "need-tls-phase2-secrets-blob-key",
+	        "hints should be NULL since no secrets were required");
+
+	/* Connection is good; clear secrets and ensure private key is then required */
+	nm_connection_clear_secrets (connection);
+
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name != NULL,
+	        "need-tls-phase2-secrets-blob-key",
+	        "unexpected secrets success");
+	ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+			"need-tls-phase2-secrets-blob-key",
+			"unexpected setting secrets required");
+
+	ASSERT (hints != NULL,
+	        "need-tls-phase2-secrets-blob-key",
+	        "expected returned secrets hints");
+	ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY),
+			"need-tls-phase2-secrets-blob-key",
+			"expected to require private key, but it wasn't");
+
+	g_object_unref (connection);
+
+	/*** Just clear the private key this time ***/
+
+	connection = make_tls_phase2_connection ("need-tls-phase2-secrets-blob-key-password",
+	                                         NM_SETTING_802_1X_CK_SCHEME_BLOB);
+	ASSERT (connection != NULL,
+	        "need-tls-phase2-secrets-blob-key-password",
+	        "error creating test connection");
+
+	s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+	ASSERT (s_8021x != NULL,
+	        "need-tls-phase2-secrets-blob-key-password",
+	        "error getting test 802.1x setting");
+
+	g_object_set (G_OBJECT (s_8021x), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, NULL, NULL);
+
+	/* Blobs are already decrypted and don't need a password */
+	hints = NULL;
+	setting_name = nm_connection_need_secrets (connection, &hints);
+	ASSERT (setting_name == NULL,
+	        "need-tls-phase2-secrets-blob-key-password",
+	        "unexpected secrets failure");
+	ASSERT (hints == NULL,
+	        "need-tls-phase2-secrets-blob-key-password",
+	        "hints should be NULL since no secrets were required");
+
+	g_object_unref (connection);
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	DBusGConnection *bus;
+	char *base;
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	if (!nm_utils_init (&error))
+		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+	/* The tests */
+	test_need_tls_secrets_path ();
+	test_need_tls_secrets_blob ();
+	test_need_tls_phase2_secrets_path ();
+	test_need_tls_phase2_secrets_blob ();
+
+	base = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", base);
+	g_free (base);
+	return 0;
+}
+
diff --git a/libnm-util/tests/test-setting-8021x.c b/libnm-util/tests/test-setting-8021x.c
new file mode 100644
index 00000000..6202a142
--- /dev/null
+++ b/libnm-util/tests/test-setting-8021x.c
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2009 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+
+#include "nm-test-helpers.h"
+#include <nm-utils.h>
+
+#include "nm-setting-connection.h"
+#include "nm-setting-8021x.h"
+
+static void
+compare_decrypted (const char *test,
+                   const char *decrypted_path,
+                   const GByteArray *key)
+{
+	char *contents = NULL;
+	gsize len = 0;
+	GError *error = NULL;
+	gboolean success;
+
+	success = g_file_get_contents (decrypted_path, &contents, &len, &error);
+	ASSERT (success == TRUE,
+	        test, "failed to read decrypted key file: %s", error->message);
+
+	ASSERT (len > 0, test, "decrypted key file invalid (size 0)");
+
+	ASSERT (len == key->len,
+	        test, "decrypted key file (%d) and decrypted key data (%d) lengths don't match",
+	        len, key->len);
+
+	ASSERT (memcmp (contents, key->data, len) == 0,
+	        test, "decrypted key file and decrypted key data don't match");
+
+	g_free (contents);
+}
+
+static void
+test_private_key_import (const char *path,
+                         const char *password,
+                         const char *decrypted_path,
+                         NMSetting8021xCKScheme scheme)
+{
+	NMSetting8021x *s_8021x;
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+	GError *error = NULL;
+
+	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+	ASSERT (s_8021x != NULL, "private-key-import", "setting was NULL");
+
+	success = nm_setting_802_1x_set_private_key (s_8021x,
+	                                             path,
+	                                             password,
+	                                             scheme,
+	                                             &format,
+	                                             &error);
+	ASSERT (success == TRUE,
+	        "private-key-import", "error reading private key: %s", error->message);
+
+	if (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+	    && format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+		const GByteArray *key;
+
+		ASSERT (decrypted_path != NULL, "private-key-import", "missing decrypted key file");
+
+		key = nm_setting_802_1x_get_private_key_blob (s_8021x);
+		ASSERT (key != NULL, "private-key-import", "missing private key blob");
+		compare_decrypted ("private-key-import", decrypted_path, key);
+	}
+
+	g_object_unref (s_8021x);
+}
+
+static void
+test_phase2_private_key_import (const char *path,
+                                const char *password,
+                                const char *decrypted_path,
+                                NMSetting8021xCKScheme scheme)
+{
+	NMSetting8021x *s_8021x;
+	gboolean success;
+	NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+	GError *error = NULL;
+
+	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+	ASSERT (s_8021x != NULL, "phase2-private-key-import", "setting was NULL");
+
+	success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+	                                                    path,
+	                                                    password,
+	                                                    scheme,
+	                                                    &format,
+	                                                    &error);
+	ASSERT (success == TRUE,
+	        "phase2-private-key-import", "error reading private key: %s", error->message);
+
+	if (   scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+	    && format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+		const GByteArray *key;
+
+		ASSERT (decrypted_path != NULL, "phase2-private-key-import", "missing decrypted key file");
+
+		key = nm_setting_802_1x_get_phase2_private_key_blob (s_8021x);
+		ASSERT (key != NULL, "phase2-private-key-import", "missing private key blob");
+		compare_decrypted ("phase2-private-key-import", decrypted_path, key);
+	}
+
+	g_object_unref (s_8021x);
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	DBusGConnection *bus;
+	char *base;
+	const char *decrypted = NULL;
+
+	if (argc < 3)
+		FAIL ("init", "need at least two arguments: <path> <password> [decrypted private key]");
+
+	if (argc == 4)
+		decrypted = argv[3];
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	if (!nm_utils_init (&error))
+		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+	/* The tests */
+	test_private_key_import (argv[1], argv[2], NULL, NM_SETTING_802_1X_CK_SCHEME_PATH);
+	test_phase2_private_key_import (argv[1], argv[2], NULL, NM_SETTING_802_1X_CK_SCHEME_PATH);
+
+	test_private_key_import (argv[1], argv[2], decrypted, NM_SETTING_802_1X_CK_SCHEME_BLOB);
+	test_phase2_private_key_import (argv[1], argv[2], decrypted, NM_SETTING_802_1X_CK_SCHEME_BLOB);
+
+	base = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", base);
+	g_free (base);
+	return 0;
+}
+
diff --git a/libnm-util/tests/test-settings-defaults.c b/libnm-util/tests/test-settings-defaults.c
new file mode 100644
index 00000000..9f38a145
--- /dev/null
+++ b/libnm-util/tests/test-settings-defaults.c
@@ -0,0 +1,135 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+
+#include "nm-test-helpers.h"
+#include <nm-utils.h>
+
+#include "nm-setting-8021x.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-serial.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+
+
+static void
+test_defaults (GType type, const char *name)
+{
+	GParamSpec **property_specs;
+	guint n_property_specs;
+	GObject *setting;
+	int i;
+
+	setting = g_object_new (type, NULL);
+
+	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+	ASSERT (property_specs != NULL,
+	        name, "couldn't find property specs for object of type '%s'",
+	        g_type_name (G_OBJECT_TYPE (setting)));
+
+	for (i = 0; i < n_property_specs; i++) {
+		GParamSpec *prop_spec = property_specs[i];
+		GValue value = { 0, };
+		GValue defvalue = { 0, };
+		char *actual, *expected;
+		gboolean ok = FALSE;
+
+		/* Ignore non-fundamental types since they won't really have
+		 * defaults.
+		 */
+		if (!G_TYPE_IS_FUNDAMENTAL (prop_spec->value_type))
+			continue;
+
+		g_value_init (&value, prop_spec->value_type);
+		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+
+		g_value_init (&defvalue, prop_spec->value_type);
+		g_param_value_set_default (prop_spec, &defvalue);
+
+		actual = g_strdup_value_contents (&value);
+		expected = g_strdup_value_contents (&defvalue);
+
+		if (!strcmp (prop_spec->name, NM_SETTING_NAME)) {
+			/* 'name' is always the setting name, not the default value */
+			ok = !strcmp (nm_setting_get_name (NM_SETTING (setting)), name);
+			g_free (expected);
+			expected = g_strdup (name);
+		} else
+			ok = g_param_value_defaults (prop_spec, &value);
+
+		ASSERT (ok,
+		        name, "property '%s' value '%s' not the expected default value '%s'",
+		        prop_spec->name, actual, expected);
+
+		g_free (actual);
+		g_free (expected);
+		g_value_unset (&value);
+		g_value_unset (&defvalue);
+	}
+
+	g_free (property_specs);
+	g_object_unref (setting);
+}
+
+int main (int argc, char **argv)
+{
+	GError *error = NULL;
+	DBusGConnection *bus;
+	char *base;
+
+	g_type_init ();
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+	if (!nm_utils_init (&error))
+		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+	/* The tests */
+	test_defaults (NM_TYPE_SETTING_CONNECTION, NM_SETTING_CONNECTION_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_802_1X, NM_SETTING_802_1X_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_CDMA, NM_SETTING_CDMA_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_GSM, NM_SETTING_GSM_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_IP4_CONFIG, NM_SETTING_IP4_CONFIG_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_PPP, NM_SETTING_PPP_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_PPPOE, NM_SETTING_PPPOE_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_SERIAL, NM_SETTING_SERIAL_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_VPN, NM_SETTING_VPN_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_WIRED, NM_SETTING_WIRED_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_WIRELESS, NM_SETTING_WIRELESS_SETTING_NAME);
+	test_defaults (NM_TYPE_SETTING_WIRELESS_SECURITY, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+
+	base = g_path_get_basename (argv[0]);
+	fprintf (stdout, "%s: SUCCESS\n", base);
+	g_free (base);
+	return 0;
+}
+