Just An Application

December 17, 2009

What’s New In MIDP 3.0: Redux – Security For MIDP Applications

Filed under: Java, JME, MIDP, MIDP Security, MIDP3, MIDP3Issues — Tags: , , , , , — Simon Lewis @ 12:35 pm

Changes Since Proposed Final Draft

The Security For MIDP Applications chapter, now only available as chapter six of the PDF version of the documentation, has acquired an entirely new section entitled Combined usage of MIDP 2.0 and MIDP 3.0 security models

This states that a MIDP 3.0 MIDlet Suite can use both the new

  • MIDlet-Permission-<n>, and
  • MIDlet-Permission-Opt-<n>

attributes, and the legacy MIDP 2.0

  • MIDlet-Permissions, and
  • MIDlet-Permissions-Opt

attributes.

Similarly a LIBlet can use the use

  • LIBlet-Permission-<n>, and
  • LIBlet-Permission-Opt-<n>

attributes, and the newly defined

  • LIBlet-Permissions
  • LIBlet-Permissions-Opt

attributes.

This addition is presumably motivated by the fact that currently the vast majority of auxiliary MIDP compliant JME JSRs have not been updated to define fine-grained permissions for use with MIDP 3.0.

Issues

Underspecification

The new section says nothing more than that the attributes can all be used together. As such the feature is a tad underspecified. There are a number of possible use cases which need to be addressed. For example

  1. Can a MIDP 3.0 MIDlet Suite use only the legacy attributes ?

  2. Can a MIDP 3.0 MIDlet Suite use named permissions to request fine-grained permissions ?

Runtime Named Permission Checking

Although a MIDP 3.0 MIDlet Suite can now request named permissions, as the specification stands, at runtime, a MIDlet contained in such a MIDlet Suite has no way of checking the named permissions since it cannot use the deprecated MIDlet.checkPermission(String) method. The specification still
states that

For MIDP 3.0 applications, calling checkPermission throws an exception.

Legacy MIDlet Suites Can Use Either Pair Of Attributes But Not Both

The section entitled Security for MIDP 2.x MIDlet Suites, which is now only available as Section 9.2.2 of the PDF version of the documentation has not been changed.

It still states that

  • If there exists one or more instances of the MIDlet-Permission-<n> or MIDlet-Permission-Opt-<n> attribute in the JAD or JAR Manifest, then the Permission classes associated with these attribute values MUST be used, and any instances of MIDlet-Permissions or MIDlet-Permissions-Opt MUST be ignored.

  • If there exists no instances of MIDlet-Permission-<n> or MIDlet-Permission-Opt-<n> attribute in JAD or JAR Manifest, then the following MIDP 2.0 specific attributes MUST be processed when they appear in the JAD or JAR Manifest :

    • MIDlet-Permissions — attribute contains a list of one or more permissions. Multiple permissions are separated by a comma (Unicode U+002C). Leading and trailing whitespace (Unicode U+0020) and tabs (Unicode U+0009) are ignored. The permissions are critical to the function of the MIDlet suite and it will not operate correctly without them.
    • MIDlet-Permissions-Opt — attribute contains a list of one or more permissions. Multiple permissions are separated by a comma (Unicode U+002C). Leading and trailing whitespace (Unicode U+0020) and tabs (Unicode U+0009) are ignored. The permissions are not critical to the function of the MIDlet suite and it will operate correctly without them.

The motivation for defining this functionality was never entirely obvious. As it stands a MIDP 2.x MIDlet Suite can continue to use named permissions in which case it can also use any MIDP compliant auxiliary JME JSR, or use fine-grained permissions in which case it can only use those MIDP compliant auxiliary JME JSRs which have defined fine-grained permissions. Currently, it is diffcult to see why any developer would make the second choice

Given that an implementation now has to support a hybrid fine-grained/named permission model for MIDP 3.0 MIDlet Suites it is unclear why it cannot support it for MIDP 2.x MIDlet Suites as well. Either that or not allow them to use the MIDP 3.0 permissions in the first place.


Copyright (c) 2009 By Simon Lewis. All Rights Reserved.

July 25, 2009

What’s New In MIDP 3.0 ? Part 44: The Security Policy And Fine-Grained Permissions

Filed under: Java, JME, MIDP, MIDP Security, MIDP3, MIDP3Issues — Tags: , , , , , — Simon Lewis @ 2:32 pm

1. Mapped And Unmapped Permissions

The default MIDP 3.0 security policy divides the defined fine-grained permissions into those that it maps to function groups, and those that it does not.

The latter it further divides into

  • Allowed

  • Not Allowed

  • Not Permitted

where Not Permitted means a permission

MUST NOT be mapped to any function group, and MUST NOT be available in either the Identified or Unidentified Third Party Protection Domain.

2. The Unmapped Permissions

Omitting the CDC specific permissions the unmapped permissions are

  • java.lang.RuntimePermission

  • java.util.PropertyPermission

  • javax.microedition.event.EventPermission

  • javax.microedition.midlet.ActionsDeniedPermission

  • javax.microedition.midlet.AutoStartPemission

2.1 Manufacturer And Operator Protection Domains

By implication, they are granted all the permissions in function groups, MIDlet Suites bound to the Manaufacturer or Operator protection domains are granted all the unmapped permissions.

Note

    It is not clear whether this would include a RuntimePermission with a target of exitVM. Possibly not ?

2.2 Identified Third Party Protection Domain

2.2.1 ActionsDeniedPermission

The ActionsDeniedPermission is ‘Not Permitted’.

MIDlet Suites bound to the Identified Third Party protection domain cannot use the

    MIDlet-UserDenied

or

    MIDlet-<n>-UserDenied

attributes.

2.2.2 AutoStartPermission

The AutoStartPermission is ‘Not Permitted’.

MIDlet Suites bound to the Identified Third Party protection domain cannot use the

    MIDlet-<n>-Type

attribute with a type of

    autostart

2.2.3 EventPermission

The apparent intention of the security policy is that MIDlet Suites bound to the Identified Third Party protection domain are granted an EventPermission with an action of

  • read for any Event

  • register for any Event

  • post for any non-system Event

but see Issues.

MIDlets in these MIDlet Suites can successfully call

  • the EventManager.getCurrent(String) method for any event.

  • any of the EventManager.addListener() methods for any event

  • any of the EventManager.registerApplication() methods for any event

  • the EventManager.post(EventData) method for any non-system event

and use the

    MIDlet-Event-Launch-<n>

attribute for any event.

2.2.4 PropertyPermission

MIDlet Suites bound to the Identified Third Party protection domain are granted a PropertyPermission with an action of read for any system property, but see Issues.

MIDlets in these MIDlet Suites can use the System.getProperty(String) method to get any system property.

2.2.5 RuntimePermission

The RuntimePermission is ‘Not Permitted’.

2.3 Unidentified Third Party Protection Domain

2.3.1 ActionsDeniedPermission

The ActionsDeniedPermission is ‘Not Permitted’.

MIDlet Suites bound to the Unidentified Third Party protection domain cannot use the

    MIDlet-UserDenied

or

    MIDlet-<n>-UserDenied

attributes.

2.3.2 AutoStartPermission

The AutoStartPermission is ‘Not Permitted’.

MIDlet Suites bound to the Identified Third Party protection domain cannot use the

    MIDlet-<n>-Type

attribute with a type of

    autostart

2.3.3 EventPermission

MIDlet Suites bound to the Unidentified Third Party protection domain are granted an EventPermission with an action of

  • read for any Event

  • register for any Event

MIDlets in these MIDlet Suites can successfully call

  • the EventManager.getCurrent(String) method for any Event.

  • any of the EventManager.addListener() methods for any Event

  • any of the EventManager.registerApplication() methods for any Event

and use the

    MIDlet-Event-Launch-<n>

attribute for any event, but they cannot post any kind of Event.

2.3.4 PropertyPermission

The apparent intention of the security policy is that MIDlet Suites bound to the Unidentified Third Party protection domain are granted an PropertyPermission with an action of read for any system property except those with a prefix of

    microedition.deviceid.

or

    microedition.subscriberid.

but see Issues.

2.3.5 RuntimePermission

The RuntimePermission is ‘Not Permitted’.

3. Issues

3.1 EventPermission

The security policy lists the following EventPermissions

  1. EventPermission("*", "read")

  2. EventPermission("*", "register")

  3. EventPermission("*.*", "post")

Numbers 1 and 2 are ‘Allowed’ for both the Identified and Unidentified Third Party protection domains.

Number 3 is ‘Allowed’ for the Identified Third Party protection domain and ‘Not Allowed’ for the Unidentified Third Party protection domain.

The form of the target name for Number 3

    *.*

is presumably intended not to match the names of system Events, thereby preventing MIDlets in MIDlet Suites bound to the Identified
Third Party protection domain from posting them.

Unfortunately the class documentation for EventPermission currently defines the target name as follows

The target name is the name of the event (“BATTERY_LEVEL”, “com.MyCompany.MyEvent”, etc). The naming convention follows the hierarchical property naming convention and are explained in the package description. An asterisk MAY appear at the end of the event name, following a “.”, or by itself, to signify a wildcard match. For example: “com.MyCompany.*” or “*” is valid, but “*MyCompany” or “a*b” is not valid.

so

    *.*

is not actually legal.

3.2 PropertyPermision

The security policy lists the following PropertyPermissions

  1. PropertyPermission("microedition.deviceid.*", "read")

  2. PropertyPermission("microedition.subscriberid.*", "read")

  3. PropertyPermission("microedition.locale", "read")

  4. PropertyPermission("microedition.profile", "read")

  5. PropertyPermission("microedition.platform", "read")

  6. PropertyPermission("microedition.*", "read")

Numbers 1 and 2 are described as not granted to MIDlet Suites bound to the Unidentified Third Party protection domain.

This however is somewhat moot given the presence of number 6.

Try running the following using a JSE distribution and you will see why.


    package scratch.propertypermission;

    import java.util.PropertyPermission;

    public final class PropertyPermissionTest
    {
        public static void main(String[] theArgs)
        {
            PropertyPermission pp = new PropertyPermission("microedition.*", "read");
		
            for (int i = 0; i < PROPERTY_NAMES.length; i++)
            {
                String property = PROPERTY_NAMES[i];
			
                if (pp.implies(new PropertyPermission(property, "read")))
                {
                    System.out.print("Can read ");
                }
                else
                {
                    System.out.print("Cannot read ");
                }
                System.out.println(property);
            }
        }

        private static final String[] PROPERTY_NAMES = 
        {
            "microedition.deviceid.uuid",
            "microedition.deviceid.imei",
            "microedition.deviceid.esn",
            "microedition.deviceid.meid",
            "microedition.deviceid.pesn",
            "microedition.subscriberid.uuid",
            "microedition.subscriberid.imsi",
            "microedition.subscriberid.msisdn",
            "microedition.subscriberid.iccid",
            "microedition.subscriberid.euimid",
            "microedition.locale",
            "microedition.profiles",
            "microedition.platform",
            "microedition.commports",
            "microedition.hostname"
        };
    }


Possibly this usage is intended as a notational shorthand, but if so it is a very misleading one.


Copyright (c) 2009 By Simon Lewis. All Rights Reserved

July 24, 2009

What’s New In MIDP 3.0 ? Part 43: Multiply Signed MIDlet Suites

Filed under: Java, JME, MIDletSuite, MIDP, MIDP Security, MIDP3 — Tags: , , , , , — Simon Lewis @ 4:30 pm

A MIDlet Suite may be signed using multiple distinct private keys.

This makes it posssible both

  • for the same entity to sign a MIDlet Suite multiple times, and

  • for different entities to sign the same MIDlet Suite

The first case means it is possible for a MIDlet Suite develper to obtain signing certificates from different sources, for example, network operators, and then sign a MIDlet Suite using the private key associated with each issued signing certificate. This in turn makes it possible to use the same signed MIDlet Suite on different operator networks, rather than having to re-package the MIDlet Suite for each network.

The second case makes it possible for a MIDlet Suite developer to sign a MIDlet Suite once, or multiple times as above, and then submit it to a third-party responsible for auditing or verifying its behaviour who can then sign it themselves.

The MIDlet-Jar-RSA-SHA1-<n> Attribute

A signature of a MIDlet Suite is specified using the

    MIDlet-Jar-RSA-SHA1-<n>

attribute.

The value of the attribute should be the Base-64 encoded signature of the MIDlet Suite JAR.

The canonical rules for ordinal based attributes apply. The first ordinal must be one (1). Any attribute after the first gap in the sequence is ignored.

For each signature there should be an associated certificate chain specified using one or more

    MIDlet-Certificate-<n>-<m>

attributes with the value of n in the certificate chain attributes corresponding to the value of n in the signature attribute.

The number of certificate chains must equal the number of signatures or the installation of the MIDlet Suite will fail.

Multiply Signing MIDP 2.x MIDlet Suites

Existing MIDP 2.x MIDlet Suites can also be multiply signed. If the

    Microedition-Profile

attribute specifies either

  • MIDP-2.0, or

  • MIDP-2.1

then MIDlet-Jar-RSA-SHA1-<n> attributes take precedence over the MIDlet-Jar-RSA-SHA1 attribute. If only the latter attribute is present then it it processed using the legacy MIDP 2.x authentication and verification algorithm.


Copyright (c) 2009 By Simon Lewis. All Rights Reserved

June 15, 2009

What’s New In MIDP 3.0 ? Part 9: Fine-Grained Permissions

Filed under: Java, JME, MIDP, MIDP Security, MIDP3, Security — Tags: , , , , , , — Simon Lewis @ 2:01 pm

The introduction of support for the JSE fine-grained permission security model in CLDC 1.1.1 (JSR139) has enabled MIDP 3
to modify the existing MIDP 2 permission model by substituting fine-grained permissions for the named permissions used previously.

1. Permission Classes

The JSE fine-grained permission security model is based on the definition of permission classes, that is, Java classes which inherit directly or indirectly from the class java.security.Permission. The model is programmatic and hence extensible both in terms of how a security policy is defined and enforced, and in terms of the semantics of permissions and how they can be parameterized.

The most common parameterizations of permission classes are those enable the specification of the set of entities to which they apply, and in some cases, in addition, the set of actions on those entities which the permission grants, but essentially arbitrary parameterization and permission semantics is possible as long as someone is prepared to write the code.

The MIDP 3 security model uses the permission classes defined in either CLDC 1.1.1 or CDC in conjunction with MIDP 3 specific permission classes it defines itself.

Note

The permission classes defined in the CLDC 1.1.1 version of the javax.microedition.io package currently have no counterparts in the CDC version of the package. It is not clear what the implications of this are for MIDP 3 running on top of CDC and MIDlet Suites/LIBlets targetted at CDC.

1.1 CLDC 1.1.1 Permission Classes

1.1.1 java.lang

RuntimePermission

A RuntimePermission controls access to runtime thread modification and vm exit.

The permission is parameterized by identifying the functionality to control. The possible values are the Strings

    "exitVM"

or

    "modifyThread"

or

    "*"

which matches all possible values.

There are no defined actions. The permission is either granted or denied.

1.1.2 java.util

PropertyPermission

A PropertyPermission controls access to a system property or properties.

It is parameterized on the name of the system property or system properties, to which it controls access, and on the actions which may be performed.

The system property or system properties can be specified by name, using a pattern, or as a wildcard which matches all properties.

The possible actions are specified as read or write. These allow the methods System.getProperty(String) and System.setProperty(String) respectively, to be called.

Note

It is unclear why the write action is defined as the method System.setProperty(String) does not exist in CLDC.

To successfully call System.getProperty(String) for a given property, the caller must have been granted a PropertyPermission which allows the read action for that property specifically, for a set of properties which includes that property, or for all properties.

1.1.3 javax.microedition.io

The javax.microedition.io package contains permission classes which control the opening of connections via the variants of the Connector.open(). method. Permission classes are defined on a per-connection URI scheme basis as follows.

Scheme Class
comm CommProtocolPermission
datagram DatagramProtocolPermission
file FileProtocolPermission
http HttpProtocolPermission
https HttpsProtocolPermission
socket SocketProtocolPermission
ssl SSLProtocolPermission

All the permission classes are parameterized on the set of connections they allow to be opened. The specification of the set of permitted connections is protocol specific, but in the case of the IP based protocols, for example, permitted hosts and/or ports or port ranges can be specified.

The FileProtocolPermission supports additional parameterization of the file access mode granted.

1.2.2 MIDP 3.0 Permission Classes

MIDP 3.0 augments the permission classes defined in CLDC 1.1.1 or CDC with permission classes for controlling access to MIDP specific functionality.

1.2.2.1 javax.microedition.event

EventPermission

An EventPermission controls runtime access to the functionality provided by the Events API. See here for details.

Note

The specification only specifies the use of EventPermission at runtime. I would assume it is also intended to be used to control the installation time registration of Event Handlers.

1.2.2.2 javax.microedition.io

PushRegistryPermission

A PushRegistryPermission controls access to the PushRegistry functionality, both at installation time
and runtime.

The permission is parameterized in terms of the URI scheme of the push connections to which it applies,
and the actions that are granted on those push connections.

The URI specifies the scheme to which the permission applies, and must be of the form

    <scheme> ":"

Alternatively the wildcard (“*”) which matches all URI schemes can be specified.

The actions which can be specified are

  • alarm
  • dynamic
  • static

which control

  • the runtime registration of alarms
  • the runtime registration of push connections
  • the installation time registration of push connections

respectively.

When requesting permission to register alarms the URI must be specified as “*”.

1.2.2.3 javax.microedition.midlet

ActionsDeniedPermission

The ActionsDeniedPermission controls the installation time use of the following attributes

  • MIDlet-UserDenied
  • MIDlet-&ltn>-UserDenied

The permission is not parameterized. It is either granted or denied. If the MIDlet Suite being installed is granted the permission then use of the attributes is allowed, otherwise it is not and installation of the MIDlet Suite will fail.

AutoStartPermission

The AutoStartPermission controls the installation time declaration of AutoStart MIDlets via the use of the

  • MIDlet-<n>-Type

attribute. The permission is not parameterized. It is either granted or denied. If the MIDlet Suite being installed is granted the permission then the attribute can be used to declare an AutoStart MIDlet, otherwise it cannot, and installation of the MIDlet Suite will faill.

1.2.3 Permission Classes For Other JSRs

Existing JSRs which currently define named permissions for use with MIDP 2.0+ will need to be updated to define equivalent permission classes for use with MIDP 3.0.

In some cases this is already happening. For example the proposed changes for the Maintenance Release of the Content Handler API (JSR211) include the definition of a ContentHandlerPermission class.

2. Installation Time Changes

2.1 MIDlet Suite Permissions

MIDlet Suites that need to do so must now request each required or optional permission at installation time using either a

    MIDlet-Permission-<n>

or a

    MIDlet-Permission-Opt-<n>

attribute respectively. The standard constraints for attribute name ordinals apply.

The value in each case is of the form

    <ClassName> [" " <Name> [" " <Actions>]]

The <ClassName> is the name of the permission class.

The <Name> identifies the entity or entities to which the permission applies, and is a quoted string.

The <Actions> are a whitespace separated list of the requested actions as specified for the given permission class. Each action is a quoted string.

The <Actions> can only be omitted if the specified permission class has a one argument constructor.

The <Name> and <Actions> can only be omitted if the specified permission class has a zero argument constructor.

As in MIDP 2, if any required permission specified by the MIDlet Suite is denied by the protection domain to which it would be bound if installed then the installation will fail.

A MIDP 3.0 MIDlet Suite must not use the MIDP 2 attributes MIDlet-Permissions and MIDlet-Permissions-Opt. Installation will fail if either of them is present.

Example

An attribute for a MIDlet Suite that needs to register AutoStart MIDlets.

    MIDlet-Permission-1: javax.microedition.midlet.AutoStartPermission

Example

An attribute for a MIDlet Suite that would like to be able to listen for datagrams on port 45000

    MIDlet-Permission-Opt-1: javax.microedition.io.DatagramProtocolPermission "datagram://:45000"

Example

An attribute for a MIDlet Suite that needs to listen for, get, and register event handlers for, all events.

    MIDlet-Permission-1: javax.microedition.event.EventPermission "*" "read" "register"

2.2 LIBlet Permissions

A LIBlet may specify its required and optional permissions in its JAD and the manifest of its Jar using

    LIBlet-Permission-<n>

and

    LIBlet-Permission-Opt-<n>

attributes respectively. The standard constraints for attribute name ordinals apply.

The values are as for the corresponding MIDlet Suite attributes.

The specification of a permission via a LIBlet-Permission-<n> or LIBlet-Permission-Opt-<n> attribute in a LIBlet which a MIDlet Suite is dependent upon is not equivalent to its specification via a MIDlet-Permission-<n> or MIDlet-Permission-Opt-<n> attribute in the MIDlet Suite itself. It does not constitute a request for a permission on behalf of the dependent MIDlet Suite. Instead a MIDlet Suite must specify all the required and optional permissions of all the LIBlets it is dependent upon itself using MIDlet-Permission-<n> or MIDlet-Permission-Opt-<n> attributes as appropriate.

Note

The specification does not require an implementation to check whether a MIDlet Suite has in fact requested all the permissions specified in the LIBlets it is dependent upon. The onus is on the MIDlet Suite developer to get it right, or to use a tool to generate the necessary MIDlet Suite attributes programatically given the LIBlet dependencies.

3. Runtime Changes

1.5.1 Permission Checking

In MIDP 2.0 runtime checking of named permissions could be performed using a MIDlet’s

     checkPermission(String)

method. This method has been deprecated. It will continue to work as before if called by a legacy MIDlet, but will otherwise throw a IllegalStateException.

Runtime permission checking can now be performed by using the method

    java.security.AccessController.checkPermission(Permission)

A fundamental and important difference between these two methods is their behaviour in the case of checking a user permission when determining the status would require interaction with a user.

Calling the MIDlet.checkPermission(String) method in this case will cause the method to return a status of -1, meaning unknown.

Calling the AccessController.checkPermission(Permission) method in case this will result in the user being prompted. It will then either return or throw a SecurityException as appropriate.

It is difficult to think of a situation in which this is actually desirable, and the specification goes as far as to say

… it is advised that MIDlets do not check permissions unless absolutely necessary.

Example


...

import java.security.AccessController;

import javax.microedition.io.PushRegistryPermission;

...

    // See if we can dynamically register socket based push connections
    try
    {
        AccessController.checkPermission(
                            new PushRegistryPermission(
                                    "socket:",
                                    "dynamic"));
        // Apparently so
        ...
    }
    catch (SecurityException se)
    {
        // Nope
        ...
    }

1.5.2 Newly Protected APIs

1.5.2.1 java.lang

1.5.2.1.1 Runtime

The method Runtime.exit() is now protected by a java.lang.RuntimePermission. Since MIDP has always specified that this method must throw a SecurityException when invoked by a MIDlet the behaviour is effectively unchanged.

1.5.2.1.2 System

The method System.exit() is now protected by a java.lang.RuntimePermission. Since MIDP has always specified that this method must throw a SecurityException when invoked by a MIDlet the behaviour is effectively unchanged.

The method System.getProperty(String) is now protected by a java.util.PropertyPermission.

Depending upon the system property or properties it is accessing a legacy MIDlet using this method may no longer work correctly.

A new MIDlet using this method will need to request the appropriate permission(s).

1.5.2.1.3 Thread

The methods Thread.interrupt() and Thread.setProperty(int) are now protected by a RutimePermission.

Should there be any legacy MIDlets using either of these methods they will no longer work correctly.

4. Backwards Compatibility

4.1 Installation Time

To support existing MIDP 2 MIDlet Suites that request named permissions, the specification defines a mapping from named permissions to corresponding permission classes and parameters.

In addition the specification requires an implementation to support the use of the MIDlet-Permission-<n> and MIDlet-Permission-Opt-<n> by a MIDlet Suite which has declared its required profile to be MIDP 2.0 or 2.1.

If these attributes are present and the MIDP 2 attributes MIDlet-Permissions and/or MIDlet-Permissions-Opt are also present, the MIDP 2 attributes will be ignored and the MIDP 3 attributes will be used instead.

If only the MIDP 2 attributes are present they will be used and the requested named permissions will be mapped to the corresponding permission classes and parameters using the defined mapping.

Note

I assume this is intended to make it possible to retrofit existing MIDP 2 MIDlet suites to request fine-grained permissions but otherwise leave them unchanged.

4.2 Runtime

As mentioned above the method MIDlet.checkPermission(String) has been deprecated. Legacy MIDlets can of course continue to call it. Its argument will be mapped to the corresponding permission class and attributes.

Also as mentioned above a handful of methods are now protected permissions. The specification does not contain any specific requirements regarding their behaviour when called by legacy MIDlets, so it is possible that a legacy MIDlet may break as a result. The most obvious candidate to cause problems is System.getProperty(String).


Copyright (c) 2009 By Simon Lewis. All Rights Reserved.

Blog at WordPress.com.

%d bloggers like this: