| /* |
| * QEMU PAM authorization driver |
| * |
| * Copyright (c) 2018 Red Hat, Inc. |
| * |
| * 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.1 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, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "authz/pamacct.h" |
| #include "trace.h" |
| #include "qemu/module.h" |
| #include "qom/object_interfaces.h" |
| |
| #include <security/pam_appl.h> |
| |
| |
| static bool qauthz_pam_is_allowed(QAuthZ *authz, |
| const char *identity, |
| Error **errp) |
| { |
| QAuthZPAM *pauthz = QAUTHZ_PAM(authz); |
| const struct pam_conv pam_conversation = { 0 }; |
| pam_handle_t *pamh = NULL; |
| int ret; |
| |
| trace_qauthz_pam_check(authz, identity, pauthz->service); |
| ret = pam_start(pauthz->service, |
| identity, |
| &pam_conversation, |
| &pamh); |
| if (ret != PAM_SUCCESS) { |
| error_setg(errp, "Unable to start PAM transaction: %s", |
| pam_strerror(NULL, ret)); |
| return false; |
| } |
| |
| ret = pam_acct_mgmt(pamh, PAM_SILENT); |
| pam_end(pamh, ret); |
| if (ret != PAM_SUCCESS) { |
| error_setg(errp, "Unable to authorize user '%s': %s", |
| identity, pam_strerror(pamh, ret)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| static void |
| qauthz_pam_prop_set_service(Object *obj, |
| const char *service, |
| Error **errp G_GNUC_UNUSED) |
| { |
| QAuthZPAM *pauthz = QAUTHZ_PAM(obj); |
| |
| g_free(pauthz->service); |
| pauthz->service = g_strdup(service); |
| } |
| |
| |
| static char * |
| qauthz_pam_prop_get_service(Object *obj, |
| Error **errp G_GNUC_UNUSED) |
| { |
| QAuthZPAM *pauthz = QAUTHZ_PAM(obj); |
| |
| return g_strdup(pauthz->service); |
| } |
| |
| |
| static void |
| qauthz_pam_complete(UserCreatable *uc, Error **errp) |
| { |
| QAuthZPAM *pauthz = QAUTHZ_PAM(uc); |
| |
| if (!pauthz->service) { |
| error_setg(errp, "The 'service' property must be set"); |
| return; |
| } |
| } |
| |
| |
| static void |
| qauthz_pam_finalize(Object *obj) |
| { |
| QAuthZPAM *pauthz = QAUTHZ_PAM(obj); |
| |
| g_free(pauthz->service); |
| } |
| |
| |
| static void |
| qauthz_pam_class_init(ObjectClass *oc, void *data) |
| { |
| UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |
| QAuthZClass *authz = QAUTHZ_CLASS(oc); |
| |
| ucc->complete = qauthz_pam_complete; |
| authz->is_allowed = qauthz_pam_is_allowed; |
| |
| object_class_property_add_str(oc, "service", |
| qauthz_pam_prop_get_service, |
| qauthz_pam_prop_set_service); |
| } |
| |
| |
| QAuthZPAM *qauthz_pam_new(const char *id, |
| const char *service, |
| Error **errp) |
| { |
| return QAUTHZ_PAM( |
| object_new_with_props(TYPE_QAUTHZ_PAM, |
| object_get_objects_root(), |
| id, errp, |
| "service", service, |
| NULL)); |
| } |
| |
| |
| static const TypeInfo qauthz_pam_info = { |
| .parent = TYPE_QAUTHZ, |
| .name = TYPE_QAUTHZ_PAM, |
| .instance_size = sizeof(QAuthZPAM), |
| .instance_finalize = qauthz_pam_finalize, |
| .class_init = qauthz_pam_class_init, |
| .interfaces = (InterfaceInfo[]) { |
| { TYPE_USER_CREATABLE }, |
| { } |
| } |
| }; |
| |
| |
| static void |
| qauthz_pam_register_types(void) |
| { |
| type_register_static(&qauthz_pam_info); |
| } |
| |
| |
| type_init(qauthz_pam_register_types); |