Add authentication support to ippeveprinter (Issue #5665)
Esse commit está contido em:
@@ -19,6 +19,7 @@ Changes in CUPS v2.3.1
|
||||
- The libusb-based USB backend now reports an error when the distribution
|
||||
permissions are wrong (Issue #5658)
|
||||
- Added paint can labels to Dymo driver (Issue #5662)
|
||||
- The `ippeveprinter` program now supports authentication (Issue #5665)
|
||||
- The `--with-dbusdir` option was ignored by the configure script (Issue #5671)
|
||||
- Sandboxed applications were not able to get the default printer (Issue #5676)
|
||||
- Log file access controls were not preserved by `cupsctl` (Issue #5677)
|
||||
|
||||
+15
-1
@@ -6,7 +6,7 @@
|
||||
.\" Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||
.\" information.
|
||||
.\"
|
||||
.TH ippeveprinter 1 "CUPS" "17 May 2019" "Apple Inc."
|
||||
.TH ippeveprinter 1 "CUPS" "2 December 2019" "Apple Inc."
|
||||
.SH NAME
|
||||
ippeveprinter \- an ipp everywhere printer application for cups
|
||||
.SH SYNOPSIS
|
||||
@@ -16,10 +16,15 @@ ippeveprinter \- an ipp everywhere printer application for cups
|
||||
] [
|
||||
.B \-\-no\-web\-forms
|
||||
] [
|
||||
.B \-\-pam\-service
|
||||
.I service
|
||||
] [
|
||||
.B \-\-version
|
||||
] [
|
||||
.B \-2
|
||||
] [
|
||||
.B \-A
|
||||
] [
|
||||
.B \-D
|
||||
.I device-uri
|
||||
] [
|
||||
@@ -89,12 +94,21 @@ Show program usage.
|
||||
.B \-\-no\-web\-forms
|
||||
Disable the web interface forms used to update the media and supply levels.
|
||||
.TP 5
|
||||
\fB\-\-pam\-service \fIservice\fR
|
||||
Set the PAM service name.
|
||||
The default service is "other".
|
||||
.TP 5
|
||||
.B \-\-version
|
||||
Show the CUPS version.
|
||||
.TP 5
|
||||
.B \-2
|
||||
Report support for two-sided (duplex) printing.
|
||||
.TP 5
|
||||
.B \-A
|
||||
Enable authentication for the created printer.
|
||||
.B ippeveprinter
|
||||
uses PAM to authenticate HTTP Basic credentials.
|
||||
.TP 5
|
||||
\fB\-D \fIdevice-uri\fR
|
||||
Set the device URI for print output.
|
||||
The URI can be a filename, directory, or a network socket URI of the form "socket://ADDRESS[:PORT]" (where the default port number is 9100).
|
||||
|
||||
+2
-2
@@ -147,7 +147,7 @@ local: ippeveprinter-static ipptool-static
|
||||
|
||||
ippeveprinter: ippeveprinter.o ../cups/$(LIBCUPS)
|
||||
echo Linking $@...
|
||||
$(LD_CC) $(ALL_LDFLAGS) -o $@ ippeveprinter.o $(DNSSDLIBS) $(LINKCUPS)
|
||||
$(LD_CC) $(ALL_LDFLAGS) -o $@ ippeveprinter.o $(DNSSDLIBS) $(PAMLIBS) $(LINKCUPS)
|
||||
$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ ippeveprinter: ippeveprinter.o ../cups/$(LIBCUPS)
|
||||
|
||||
ippeveprinter-static: ippeveprinter.o ../cups/$(LIBCUPSSTATIC)
|
||||
echo Linking $@...
|
||||
$(LD_CC) $(ALL_LDFLAGS) -o $@ ippeveprinter.o $(LINKCUPSSTATIC)
|
||||
$(LD_CC) $(ALL_LDFLAGS) -o $@ ippeveprinter.o $(PAMLIBS) $(LINKCUPSSTATIC)
|
||||
$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
|
||||
|
||||
|
||||
|
||||
+231
-7
@@ -51,6 +51,7 @@ extern char **environ;
|
||||
# include <avahi-common/error.h>
|
||||
# include <avahi-common/thread-watch.h>
|
||||
#endif /* HAVE_DNSSD */
|
||||
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
# include <sys/mount.h>
|
||||
#endif /* HAVE_SYS_MOUNT_H */
|
||||
@@ -64,6 +65,14 @@ extern char **environ;
|
||||
# include <sys/vfs.h>
|
||||
#endif /* HAVE_SYS_VFS_H */
|
||||
|
||||
#if HAVE_LIBPAM
|
||||
# ifdef HAVE_PAM_PAM_APPL_H
|
||||
# include <pam/pam_appl.h>
|
||||
# else
|
||||
# include <security/pam_appl.h>
|
||||
# endif /* HAVE_PAM_PAM_APPL_H */
|
||||
#endif /* HAVE_LIBPAM */
|
||||
|
||||
#include "printer-png.h"
|
||||
|
||||
|
||||
@@ -148,6 +157,14 @@ typedef void *ippeve_srv_t; /* Service reference */
|
||||
typedef void *ippeve_txt_t; /* TXT record */
|
||||
#endif /* HAVE_DNSSD */
|
||||
|
||||
#if HAVE_LIBPAM
|
||||
typedef struct ippeve_authdata_s /* Authentication data */
|
||||
{
|
||||
char username[HTTP_MAX_VALUE], /* Username string */
|
||||
*password; /* Password string */
|
||||
} ippeve_authdata_t;
|
||||
#endif /* HAVE_LIBPAM */
|
||||
|
||||
typedef struct ippeve_filter_s /**** Attribute filter ****/
|
||||
{
|
||||
cups_array_t *ra; /* Requested attributes */
|
||||
@@ -224,7 +241,9 @@ typedef struct ippeve_client_s /**** Client data ****/
|
||||
char uri[1024], /* Request URI */
|
||||
*options; /* URI options */
|
||||
http_addr_t addr; /* Client address */
|
||||
char hostname[256]; /* Client hostname */
|
||||
char hostname[256], /* Client hostname */
|
||||
username[HTTP_MAX_VALUE];
|
||||
/* Authenticated username, if any */
|
||||
ippeve_printer_t *printer; /* Printer */
|
||||
ippeve_job_t *job; /* Current job, if any */
|
||||
} ippeve_client_t;
|
||||
@@ -234,6 +253,7 @@ typedef struct ippeve_client_s /**** Client data ****/
|
||||
* Local functions...
|
||||
*/
|
||||
|
||||
static http_status_t authenticate_request(ippeve_client_t *client);
|
||||
static void clean_jobs(ippeve_printer_t *printer);
|
||||
static int compare_jobs(ippeve_job_t *a, ippeve_job_t *b);
|
||||
static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy);
|
||||
@@ -281,6 +301,9 @@ static ipp_t *load_legacy_attributes(const char *make, const char *model, int p
|
||||
#if !CUPS_LITE
|
||||
static ipp_t *load_ppd_attributes(const char *ppdfile, cups_array_t *docformats);
|
||||
#endif /* !CUPS_LITE */
|
||||
#if HAVE_LIBPAM
|
||||
static int pam_func(int, const struct pam_message **, struct pam_response **, void *);
|
||||
#endif /* HAVE_LIBPAM */
|
||||
static int parse_options(ippeve_client_t *client, cups_option_t **options);
|
||||
static void process_attr_message(ippeve_job_t *job, char *message);
|
||||
static void *process_client(ippeve_client_t *client);
|
||||
@@ -316,6 +339,8 @@ static AvahiClient *DNSSDClient = NULL;
|
||||
static int KeepFiles = 0, /* Keep spooled job files? */
|
||||
MaxVersion = 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
|
||||
Verbosity = 0; /* Verbosity level */
|
||||
static const char *PAMService = NULL;
|
||||
/* PAM service */
|
||||
|
||||
|
||||
/*
|
||||
@@ -371,6 +396,14 @@ main(int argc, /* I - Number of command-line args */
|
||||
{
|
||||
web_forms = 0;
|
||||
}
|
||||
else if (!strcmp(argv[i], "--pam-service"))
|
||||
{
|
||||
i ++;
|
||||
if (i >= argc)
|
||||
usage(1);
|
||||
|
||||
PAMService = argv[i];
|
||||
}
|
||||
else if (!strcmp(argv[i], "--version"))
|
||||
{
|
||||
puts(CUPS_SVERSION);
|
||||
@@ -392,6 +425,11 @@ main(int argc, /* I - Number of command-line args */
|
||||
legacy = 1;
|
||||
break;
|
||||
|
||||
case 'A' : /* -A (enable authentication) */
|
||||
if (!PAMService)
|
||||
PAMService = "other";
|
||||
break;
|
||||
|
||||
case 'D' : /* -D device-uri */
|
||||
i ++;
|
||||
if (i >= argc)
|
||||
@@ -691,6 +729,102 @@ main(int argc, /* I - Number of command-line args */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'authenticate_request()' - Try to authenticate the request.
|
||||
*/
|
||||
|
||||
static http_status_t /* O - HTTP_STATUS_CONTINUE to keep going, otherwise status to return */
|
||||
authenticate_request(
|
||||
ippeve_client_t *client) /* I - Client */
|
||||
{
|
||||
#if HAVE_LIBPAM
|
||||
/*
|
||||
* If PAM isn't enabled, return 'continue' now...
|
||||
*/
|
||||
|
||||
const char *authorization; /* Pointer into Authorization string */
|
||||
int userlen; /* Username:password length */
|
||||
pam_handle_t *pamh; /* PAM authentication handle */
|
||||
int pamerr; /* PAM error code */
|
||||
struct pam_conv pamdata; /* PAM conversation data */
|
||||
ippeve_authdata_t data; /* Authentication data */
|
||||
|
||||
|
||||
if (!PAMService)
|
||||
return (HTTP_STATUS_CONTINUE);
|
||||
|
||||
/*
|
||||
* Try authenticating using PAM...
|
||||
*/
|
||||
|
||||
authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION);
|
||||
|
||||
if (strncmp(authorization, "Basic ", 6))
|
||||
{
|
||||
fputs("Unsupported scheme in Authorization header.\n", stderr);
|
||||
return (HTTP_STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
authorization += 5;
|
||||
while (isspace(*authorization & 255))
|
||||
authorization ++;
|
||||
|
||||
userlen = sizeof(data.username);
|
||||
httpDecode64_2(data.username, &userlen, authorization);
|
||||
|
||||
if ((data.password = strchr(data.username, ':')) == NULL)
|
||||
{
|
||||
fputs("No password in Authorization header.\n", stderr);
|
||||
return (HTTP_STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
*(data.password)++ = '\0';
|
||||
|
||||
if (!data.username[0])
|
||||
{
|
||||
fputs("No username in Authorization header.\n", stderr);
|
||||
return (HTTP_STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
pamdata.conv = pam_func;
|
||||
pamdata.appdata_ptr = &data;
|
||||
|
||||
if ((pamerr = pam_start(PAMService, data.username, &pamdata, &pamh)) != PAM_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "pam_start() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
|
||||
return (HTTP_STATUS_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "pam_authenticate() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
|
||||
pam_end(pamh, 0);
|
||||
return (HTTP_STATUS_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "pam_acct_mgmt() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
|
||||
pam_end(pamh, 0);
|
||||
return (HTTP_STATUS_SERVER_ERROR);
|
||||
}
|
||||
|
||||
strlcpy(client->username, data.username, sizeof(client->username));
|
||||
|
||||
pam_end(pamh, PAM_SUCCESS);
|
||||
|
||||
return (HTTP_STATUS_CONTINUE);
|
||||
|
||||
#else
|
||||
/*
|
||||
* No authentication support built-in, return 'continue'...
|
||||
*/
|
||||
|
||||
return (HTTP_STATUS_CONTINUE);
|
||||
#endif /* HAVE_LIBPAM */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'clean_jobs()' - Clean out old (completed) jobs.
|
||||
*/
|
||||
@@ -5226,6 +5360,78 @@ load_ppd_attributes(
|
||||
#endif /* !CUPS_LITE */
|
||||
|
||||
|
||||
#if HAVE_LIBPAM
|
||||
/*
|
||||
* 'pam_func()' - PAM conversation function.
|
||||
*/
|
||||
|
||||
static int /* O - Success or failure */
|
||||
pam_func(
|
||||
int num_msg, /* I - Number of messages */
|
||||
const struct pam_message **msg, /* I - Messages */
|
||||
struct pam_response **resp, /* O - Responses */
|
||||
void *appdata_ptr)
|
||||
/* I - Pointer to connection */
|
||||
{
|
||||
int i; /* Looping var */
|
||||
struct pam_response *replies; /* Replies */
|
||||
ippeve_authdata_t *data; /* Pointer to auth data */
|
||||
|
||||
|
||||
/*
|
||||
* Allocate memory for the responses...
|
||||
*/
|
||||
|
||||
if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
|
||||
return (PAM_CONV_ERR);
|
||||
|
||||
/*
|
||||
* Answer all of the messages...
|
||||
*/
|
||||
|
||||
data = (ippeve_authdata_t *)appdata_ptr;
|
||||
|
||||
for (i = 0; i < num_msg; i ++)
|
||||
{
|
||||
switch (msg[i]->msg_style)
|
||||
{
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
replies[i].resp_retcode = PAM_SUCCESS;
|
||||
replies[i].resp = strdup(data->username);
|
||||
break;
|
||||
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
replies[i].resp_retcode = PAM_SUCCESS;
|
||||
replies[i].resp = strdup(data->password);
|
||||
break;
|
||||
|
||||
case PAM_TEXT_INFO:
|
||||
replies[i].resp_retcode = PAM_SUCCESS;
|
||||
replies[i].resp = NULL;
|
||||
break;
|
||||
|
||||
case PAM_ERROR_MSG:
|
||||
replies[i].resp_retcode = PAM_SUCCESS;
|
||||
replies[i].resp = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
free(replies);
|
||||
return (PAM_CONV_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the responses back to PAM...
|
||||
*/
|
||||
|
||||
*resp = replies;
|
||||
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
#endif /* HAVE_LIBPAM */
|
||||
|
||||
|
||||
/*
|
||||
* 'parse_options()' - Parse URL options into CUPS options.
|
||||
*
|
||||
@@ -5431,6 +5637,8 @@ process_http(ippeve_client_t *client) /* I - Client connection */
|
||||
* Clear state variables...
|
||||
*/
|
||||
|
||||
client->username[0] = '\0';
|
||||
|
||||
ippDelete(client->request);
|
||||
ippDelete(client->response);
|
||||
|
||||
@@ -5730,6 +5938,7 @@ process_ipp(ippeve_client_t *client) /* I - Client */
|
||||
ipp_attribute_t *uri; /* Printer URI attribute */
|
||||
int major, minor; /* Version number */
|
||||
const char *name; /* Name of attribute */
|
||||
http_status_t status; /* Authentication status */
|
||||
|
||||
|
||||
debug_attributes("Request", client->request, 1);
|
||||
@@ -5880,13 +6089,18 @@ process_ipp(ippeve_client_t *client) /* I - Client */
|
||||
strcmp(resource, "/ipp/print")))
|
||||
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
|
||||
name, ippGetString(uri, 0, NULL));
|
||||
else
|
||||
else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
|
||||
{
|
||||
return (respond_http(client, status, NULL, NULL, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Try processing the operation...
|
||||
*/
|
||||
|
||||
switch (ippGetOperation(client->request))
|
||||
|
||||
switch (client->operation_id)
|
||||
{
|
||||
case IPP_OP_PRINT_JOB :
|
||||
ipp_print_job(client);
|
||||
@@ -6824,6 +7038,14 @@ respond_http(
|
||||
client->operation == HTTP_STATE_OPTIONS)
|
||||
httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
|
||||
|
||||
if (code == HTTP_STATUS_UNAUTHORIZED)
|
||||
{
|
||||
char value[256]; /* WWW-Authenticate value */
|
||||
|
||||
snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
|
||||
httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
|
||||
}
|
||||
|
||||
if (type)
|
||||
{
|
||||
if (!strcmp(type, "text/html"))
|
||||
@@ -7664,10 +7886,12 @@ usage(int status) /* O - Exit status */
|
||||
{
|
||||
_cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
|
||||
_cupsLangPuts(stdout, _("Options:"));
|
||||
_cupsLangPuts(stderr, _("--help Show program help"));
|
||||
_cupsLangPuts(stderr, _("--no-web-forms Disable web forms for media and supplies"));
|
||||
_cupsLangPuts(stderr, _("--version Show program version"));
|
||||
_cupsLangPuts(stdout, _("--help Show program help"));
|
||||
_cupsLangPuts(stdout, _("--no-web-forms Disable web forms for media and supplies"));
|
||||
_cupsLangPuts(stdout, _("--pam-service service Use the named PAM service"));
|
||||
_cupsLangPuts(stdout, _("--version Show program version"));
|
||||
_cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
|
||||
_cupsLangPuts(stdout, _("-A Enable authentication"));
|
||||
_cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
|
||||
_cupsLangPuts(stdout, _("-F output-type/subtype Set the output format for the printer"));
|
||||
#ifdef HAVE_SSL
|
||||
@@ -7688,7 +7912,7 @@ usage(int status) /* O - Exit status */
|
||||
_cupsLangPuts(stdout, _("-p port Set port number for printer"));
|
||||
_cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
|
||||
_cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
|
||||
_cupsLangPuts(stderr, _("-v Be verbose"));
|
||||
_cupsLangPuts(stdout, _("-v Be verbose"));
|
||||
|
||||
exit(status);
|
||||
}
|
||||
|
||||
@@ -472,6 +472,7 @@
|
||||
278C58EA136B64B000836530 /* Kerberos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E6136B64B000836530 /* Kerberos.framework */; };
|
||||
278C58EB136B64B000836530 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E7136B64B000836530 /* Security.framework */; };
|
||||
278C58EC136B64B000836530 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E8136B64B000836530 /* SystemConfiguration.framework */; };
|
||||
279AE6F52395B80F004DD600 /* libpam.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 279AE6F42395B80F004DD600 /* libpam.tbd */; };
|
||||
27A034821A8BDC3A00650675 /* lpadmin.c in Sources */ = {isa = PBXBuildFile; fileRef = 2732E08D137A3F5200FAFEF6 /* lpadmin.c */; };
|
||||
27A034851A8BDC5C00650675 /* libcups.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72220EAE1333047D00FCA411 /* libcups.dylib */; };
|
||||
7200511218F492F200E7B81B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E5136B64AF00836530 /* CoreFoundation.framework */; };
|
||||
@@ -3307,6 +3308,7 @@
|
||||
278C58E6136B64B000836530 /* Kerberos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kerberos.framework; path = /System/Library/Frameworks/Kerberos.framework; sourceTree = "<absolute>"; };
|
||||
278C58E7136B64B000836530 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
|
||||
278C58E8136B64B000836530 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; };
|
||||
279AE6F42395B80F004DD600 /* libpam.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libpam.tbd; path = usr/lib/libpam.tbd; sourceTree = SDKROOT; };
|
||||
27A0347B1A8BDB1300650675 /* lpadmin */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lpadmin; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
27D3037D134148CB00F022B1 /* libcups2.def */ = {isa = PBXFileReference; lastKnownFileType = text; name = libcups2.def; path = ../cups/libcups2.def; sourceTree = "<group>"; };
|
||||
27F89DA21B3AC43B00E5A4B7 /* testraster.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testraster.c; path = ../cups/testraster.c; sourceTree = "<group>"; };
|
||||
@@ -4637,6 +4639,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
279AE6F52395B80F004DD600 /* libpam.tbd in Frameworks */,
|
||||
273B1ECA226B420C00428143 /* libcups.dylib in Frameworks */,
|
||||
2767FC6619267538000F61D3 /* CoreFoundation.framework in Frameworks */,
|
||||
2767FC6719267538000F61D3 /* libresolv.dylib in Frameworks */,
|
||||
@@ -5130,6 +5133,7 @@
|
||||
72220FB113330B4A00FCA411 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
279AE6F42395B80F004DD600 /* libpam.tbd */,
|
||||
2767FC591926750C000F61D3 /* CoreFoundation.framework */,
|
||||
2767FC5A1926750C000F61D3 /* libiconv.dylib */,
|
||||
2767FC5B1926750C000F61D3 /* libresolv.dylib */,
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário